python学习-pytest(五)-pytest常用方法

最近工作中用到了pytest,总结一些用法:

1. 安装:

pip install pytest

2. 运行:

pytest 默认只能识别以test_ 开头的文件和测试用例,如果pytest后面不带文件名,则默认执行当前目录下所有以test_ 开头的文件。

  • 执行某个文件里所有以 test 开头的用例:pytest test_demo.py
# test_demo.py
def test_01():
    assert 1+1 == 2

def test_02():
    assert 1+3 == 4

执行结果:

$ pytest test_demo.py
========================================= test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items

test_demo.py ..                                                                                      [100%]

======================================== 2 passed in 0.04 seconds =========================================

如果只想执行test_01,可以使用pytest test_demo.py::test_01

  • 如果用例是用类组织起来的:
# test_class.py
class TestClass(object):
    def test_one(self):
        x = "this"
        assert ‘h‘ in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, ‘check‘)

执行结果:

$ pytest test_class.py
=========================================== test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items

test_class.py .F                                                                                     [100%]

================================================= FAILURES =================================================
____________________________________________ TestClass.test_two ____________________________________________

self = <test_class.TestClass object at 0x102f46780>

    def test_two(self):
        y = "world"
>       assert ‘h‘ in y
E       AssertionError: assert ‘h‘ in ‘world‘

test_class.py:9: AssertionError
========================================== 1 failed, 1 passed in 0.08 seconds ====================================

如果一个文件里有多个类,你只想执行其中一个类里的用例:
pytest test_class.py::TestClass

  • 执行某个文件夹下所有的以 test_开头的文件:pytest testcase/
# testcase/test_demo01.py
def test_one():
    x = ‘this‘
    assert ‘h‘ in x

# testcase/test_demo02.py
def test_two():
    y = ‘world‘
    assert ‘h‘ in y

执行结果:

$ pytest testcase/
========================================== test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items

testcase/test_demo01.py .                                                                            [ 50%]
testcase/test_demo02.py F                                                                            [100%]

============================================== FAILURES ===========================================
_________________________________________________ test_two _________________________________________________

    def test_two():
        y = ‘world‘
>       assert ‘h‘ in y
E       AssertionError: assert ‘h‘ in ‘world‘

testcase/test_demo02.py:3: AssertionError
==================================== 1 failed, 1 passed in 0.09 seconds ================================
  • 执行带某个标记的用例,比如用例中带了@pytest.mark.smoke标记的用例:
# test_mark.py
@pytest.mark.smoke
def test_01():
    assert 1+1 == 2

def test_02():
    assert 1+3 == 4
$ pytest -m smoke test_mark.py
=========================================== test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items / 1 deselected / 1 selected

test_mark.py .                                                                                       [100%]

================================== 1 passed, 1 deselected in 0.03 seconds ==================================

可以看到只执行带@pytest.mark.smoke的用例。

  • 按用例函数的名称来执行:pytest -k 01 test_mark.py
$ pytest -k 01 test_mark.py
=========================================== test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items / 1 deselected / 1 selected

test_mark.py .                                                                                       [100%]

================================== 1 passed, 1 deselected in 0.01 seconds ==================================

按照用例名字来执行,只执行名字里含有01的测试用例。

3. pytest fixture

@pytest_fixture的作用是提供一个固定的参数给测试重复的使用,相当于unittest里的setupteardown.

  • pytest fixture 做函数的参数
# test_fixture.py
@pytest.fixture(scope="module")
def num():
    n = random.randint(1, 5)
    return n

def test_01(num):
    print(num)
    assert 0

def test_02(num):
    print(num)
    assert 0
  • pytest fixture的scope有class, session 和 module.
  • 此时执行可以多次执行看看两个函数里num的数值是否是一样:
$ pytest test_fixture.py
=========================================== test session starts ============================================
platform darwin -- Python 3.7.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: /Users/libo/python3workspace/learn, inifile:
plugins: rerunfailures-7.0, picked-0.4.1, parallel-0.0.9, forked-1.0.2, cov-2.7.1, allure-pytest-2.6.1
collected 2 items

test_fixture.py FF                                                                                   [100%]

================================================= FAILURES =================================================
_________________________________________________ test_01 __________________________________________________

num = 3

    def test_01(num):
        print(num)
>       assert 0
E       assert 0

test_fixture.py:12: AssertionError
------------------------------------------- Captured stdout call -------------------------------------------
3
_________________________________________________ test_02 __________________________________________________

num = 3

    def test_02(num):
        print(num)
>       assert 0
E       assert 0

test_fixture.py:16: AssertionError
------------------------------------------- Captured stdout call -------------------------------------------
3
========================================= 2 failed in 0.06 seconds =========================================
  • 多次执行可以看见两个函数里num的值是总是相同的,如果只是普通的函数调用,两个函数里的值肯定不可能总是相等的。因此pytest fixture可以用来共享数据。
  • 此外pytest fixture 还可以实现类似teardown的操作,方法是首先把return换成yield,然后把销毁的相关操作放在yield后面。
# conftest.py
import smtplib
import pytest

@pytest.fixture(scope="module")
def smtp_connection():
    smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
    yield smtp_connection  # provide the fixture value
    print("teardown smtp")
    smtp_connection.close()
# test_module.py
def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg
    assert 0  # for demo purposes

def test_noop(smtp_connection):
    response, msg = smtp_connection.noop()
    assert response == 250
    assert 0  # for demo purpose
$ pytest -s -q --tb=no test_module.py
FFteardown smtp

2 failed in 6.81 seconds

从上面的运行可以看出来,在执行完两个测试用例之后才调用yield后面的print语句。如果我们把scope改成scope="function"那么fixture的setup和teardown就会在每一个测试用例开始和结束时执行。

  • 像上面的close操作也可以使用with语句来实现:
import smtplib
import pytest

@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection  # provide the fixture value

4. pytest.mark.parametrize

pytest 内置的pytest.mark.parametrize装饰器支持将测试函数的参数的参数化。

import pytest

@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

可以直接通过@pytest.mark.parametrize传递参数,而不需要在测试函数里面通过for循环来实现。

@pytest.mark.parametrize还可以添加起来使用:

import pytest

@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
    assert x + y == 2

上面的例子里会一共有4中情况:x=0/y=2,x=0/y=3,x=1/y=2,x=1/y=3一共四种情况。

更多用法可以参考官方文档.

5. pytest.ini

pytest的主配置文件,可以改变pytest的默认行为。我们可以把经常使用的一些选项放到pytest.ini中,或者在里面添加一些markers。

# pytest.ini 文件
[pytest]
addopts =
    --verbose
    --reruns 5    # 需要安装 pytest-rerunfailures 插件
    --clean-alluredir    # 需要 allure-pytest 插件
    --alluredir=/tmp/allure-results/
markers =
    smoke: 冒烟测试用例

使用 pytest --help 可以看到常用的pytest.ini选项:

[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:

  markers (linelist)       markers for test functions
  empty_parameter_set_mark (string) default marker for empty parametersets
  norecursedirs (args)     directory patterns to avoid for recursion
  testpaths (args)         directories to search for tests when no files or directories are given in the command line.
  console_output_style (string) console output: classic or with additional progress information (classic|progress).
  usefixtures (args)       list of default fixtures to be used with this project
  python_files (args)      glob-style file patterns for Python test module discovery
  python_classes (args)    prefixes or glob names for Python test class discovery
  python_functions (args)  prefixes or glob names for Python test function and method discovery
  xfail_strict (bool)      default for the strict parameter of xfail markers when not given explicitly (default: False)
  doctest_optionflags (args) option flags for doctests
  doctest_encoding (string) encoding used for doctest files
  cache_dir (string)       cache directory path.
  filterwarnings (linelist) Each line specifies a pattern for warnings.filterwarnings. Processed after -W and --pythonwarnings.
  log_print (bool)         default value for --no-print-logs
  log_level (string)       default value for --log-level
  log_format (string)      default value for --log-format
  log_date_format (string) default value for --log-date-format
  log_cli (bool)           enable log display during test run (also known as "live logging").
  log_cli_level (string)   default value for --log-cli-level
  log_cli_format (string)  default value for --log-cli-format
  log_cli_date_format (string) default value for --log-cli-date-format
  log_file (string)        default value for --log-file
  log_file_level (string)  default value for --log-file-level
  log_file_format (string) default value for --log-file-format
  log_file_date_format (string) default value for --log-file-date-format
  addopts (args)           extra command line options
  minversion (string)      minimally required pytest version
  workers (string)         Set the max num of workers (aka processes) to start (int or "auto" - one per core)
  tests_per_worker (string) Set the max num of concurrent tests for each worker (int or "auto" - split evenly)
  • addopts:更改命令行的默认行为,可以把一些总是用到的选项添加到这里,这样就不用每次执行的时候都输入该命令。
  • markers:自定义的标记,可以使用pytest --markers看到。
  • pytest.ini文件放到工程的目录即可。

作者:利欧leo丶
链接:https://www.jianshu.com/p/ab8e87547676
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原文地址:https://www.cnblogs.com/zhaocbbb/p/12543527.html

时间: 2024-10-09 09:51:05

python学习-pytest(五)-pytest常用方法的相关文章

python学习第五天 - for...in...循环

for..in语句是另一个循环语句,它迭代一个对象的序列,例如经历序列中的第一项.在后面的章节,我们将会看到更多关于序列的细节.现在,你需要知道的是一个序列只是一个有序的项目的集合. 例如 (保存为 for.py): for i in range(1,5): print(i) else: print('for循环结束') >>> ================================ RESTART ================================ >&g

python学习笔记(五):装饰器、生成器、内置函数、json

这周学习了装饰器和生成器,写下博客,记录一下装饰器和生成器相关的内容. 一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面每个函数都加一个功能,用来统计每个函数的运行时间是多少,找出来运行比较慢的函数,来优化代码,就需要添加一个新的功能,来统计程序的运行时间,那这样的话,就得修改每个函数了,需要改代码,但是代码特别多,改完了公司倒闭了,这时候装饰

Python学习笔记五_数据类型(字符串)

已经学习过的数据类型包括:int.float.list.tuple.dict.这篇来单独记录下字符串.单独将字符串这种数据类型写出来,是因为python里有很多非常有用的字符串内置方法. 一.字符串定义 字符串可以存任意类型的字符串,比如名字,一句话等等. 1 name = 'Rebecca' 2 msg = 'Fighting' 二.字符串内置方法 1.常用方法 输出为字符串的方法 a = 'my name is Rebecca' print(a.capitalize()) #将字符串的第一个

Python学习十五:sorted()

sorted()是Python内置的一个高阶函数,可以实现对list的排序,它还可以接收一个比较函数来实现自定义的排序. 现在有一个list : [36, 5, 12, 9, 21] 我们分别对这个list进行如下操作: 1.对list进行排序,使用sorted直接排序即可: print sorted([36, 5, 12, 9, 21]) 2.对list进行倒序排序,可以自定一个方法: def reversed_cmp(x, y): if x > y: return -1 if x < y:

Python学习(五)函数 —— 内置函数 lambda filter map reduce

Python 内置函数 lambda.filter.map.reduce Python 内置了一些比较特殊且实用的函数,使用这些能使你的代码简洁而易读. 下面对 Python 的 lambda.filter.map.reduce 进行初步的学习. lambda 匿名函数 lambda语句中,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值. lambda语句构建的其实是一个函数对象,参考下例来感受下 lambda 匿名函数: 1 def f(i): # 用户自定义返回平方数 2 retur

python学习--第五周

本节大纲: 模块介绍 time &datetime模块 random os sys shutil json & picle shelve xml处理 yaml处理 configparser hashlib subprocess logging模块 re正则表达式 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.p

python学习(五)

第五章 条件.循环和其他语句 5.1 print和import的更多信息 5.1.1 使用逗号输出 >>> print('age',43,45)         // 可以用逗号隔开多个表达式,中间会有空格age 43 45 5.1.2 把某事件作为另外事件的导入 import somemodule from somemodule improt aaa, bbb, ccc from somemodule import * 如果两个模块有同名函数怎么办? 第一种方法可以用模块引用: mod

Python学习笔记五(模块与包)

一.模块 1.模块介绍 一个模块就是包含了一组功能的python文件,可以通过import导入模块使用. python中模块分为四个类别: a) 使用python编写的.py文件 b) 已被编译为共享库或DLL的C或C++扩展 c) 把一系列模块组织到一起的文件夹,文件夹内有__init__.py文件,称该文件夹为包 d) 使用C编写并链接到python解释器的内置模块 定义my_module.py模块,模块名为my_module print("from my_module.py")

python学习十五天

内置函数二 1.lamda匿名函数 2. sorted() 3. filter() 4. map() 5. 递归函数 一. lamda匿名函数 为了解决一些简单的需求而设计的一句话函数 # 计算n的n次方 def func(n): eturn n**n print(func(10)) f = lambda n: n**n print(f(10)) lambda表示的是匿匿名函数. 不需要用def来声明, 一句句话就可以声明出一个函数 语法: 函数名 = lambda 参数: 返回值 注意: 1.

Python学习第五天

复习内容: · 迭代器&生成器 · 装饰器 · Json & pickle 数据序列化 · 软件目录结构规范yi 一.生成器 1.   列表生成式: 2.   生成器的定义:在Python中一边循环一边计算的机制成为生成器(generator). 3.   创建生成器(generator)的方法: 1.把一个列表生成式的[]修改为()即创建了一个generator. 2.一个函数定义中包含yield关键字,则可以默认为是一个generator. 4.generator与函数的区别: gen