自动化测试pytest
Pytest 命名规范
类名必须Test开头
类里方法名必须 test_开头或者 _test 结尾
文件名必须 test_开头
如果想要修改默认的配置,则需要创建一个 pytest.ini 配置文件。无论是主函数模式运行,还是命令行运行,都会读这个配置文件。
入门示例
创建一个all.py文件,同时创建一个user文件夹
在user文件夹下,创建一个 test_get_user.py文件
此时pytest会执行 user 文件夹下的所有测试case,当然如果main 函数里不指定参数也是可以的,则会执行当前文件夹以及遍历其子文件夹下的所有测试案例。
如果使用pytest cli那么命令为 pytest -sv ./user
如果想要用多线程的话,可以加上 -n 参数,比如 pytest -sv ./user -n 2
如果想要让测试案例失败的时候,重跑,那么main函数里可以加 "--reruns=2",这样当失败的时候,会再重试2次(一共3次)
简化版
比如下面的目录结构,mymath.py里只有一个 add(num1, num2) 函数,在pycharm里,将 src 和test所在的目录,设置为 Source Root,
all.py 的内容为
那么只需要执行下面的测试命令即可
coverage
使用coverage可以轻松查看pytest的覆盖率
pytest 使用
改变执行顺序
通过pytest.mark.run 来改变之行顺序,默认情况下,pytest是按函数名从上往下之行的。这个跟unitest不同,unitest是根据函数名ascii顺序执行。
分组执行
如果我们的测试比较复杂,分不同的模块(文件夹),下面分别又有不同的测试样例,我们想测试某些文件夹下特定的某几个文件。比如执行冒烟测试,分接口测试等。
此时可以在需要进行测试的函数上面,通过 @pytest.mark.xxxx 做标记,注意xxx是可以自己随便写的,比如我们写smoke,usermanage, productmanage,即使分布在不同的模块下,不同的函数里,我们也可以在测试的时候,来执行那类标记被执行
另外,还需要创建一个 pytest.ini 文件,将这些自定义marker注册进去
执行的时候输入 pytest -m "smoke" test.py 则只执行 smoke里的样例。如果想一次性执行两个,则是 pytest -m "smoke or usermanage" test.py
跳过
如果想要跳过某一个测试用例,则用 pytest.mark.skip 就可以了,这样默认就不执行了
在跳过的时候,还可以指定条件
pytest.fixture 与setup/teardown
setup/teardown 与 setup_class 与 teardown_class
这个方法比较直接,适合简单测试场景 在pytest里面,如果想要在执行测试前,和测试后,做一些事情。比如在测试前,第一步要打开浏览器,测试完成,最后一步关闭浏览器,那么我们就要用到 setup_method和 teardown_method的函数(对每一个测试用例,即每一个函数都执行前后都会执行这个操作),注意函数名必须这么写(小写),如果函数在类里(比如日志对象,数据库连接等),则是 setup_class 和 teardown_class(在类初始化以及执行完成只做一次)。示例:
pytest.fixture
pytest.fixture适合更灵活,需要复用参数的复杂测试场景。在fixture里,函数名可以随便起,如果后面的参数想自动引用,则 autouse=True,如果没有,需要将这个函数名作为参数,传给后面真正的方法。 在fixture里,yield相当于会把函数分成两部分,上面是setup,后面是 teardown
不过对于改环境变量,更好的办法是通过 monkeypatch的方法。如果用上面的方法,需要显性的删除 os.environ,并且所有的测试单元里,用的是同一份环境变量,这意味着,如果某一个单元测试改了环境变量的值,会影响另外一个单元测试。如果用下面的 monkeypatch,则所有的单元测试的环境变量是独立的。monkeypatch除了能改环境变量外,还能改某一个函数的返回值,某一个json的key或value等
使用 mock 方法来patch 函数返回值
pytest 可以和 unittest 结合使用,比如当我们在 lambda_function.py 文件里,有一个 get_string_data 的函数,这个函数需要访问外部数据,但我们每次测试的时候,不想让这个接口访问外部数据,那么就可以用 mock的 patch方法,来模拟返回值。在模拟的时候,可以用with上下文管理器结合yield方法,也可以直接用装饰器。两种用法都是可以的
使用 MagicMock 来指定mock一个类函数或方法
当我们需要mock一个类或函数的时候,我们想要模拟这个类方法的行为,比如requests.Session().get()的这个方法,我们模拟的 requests.Session()是有 get()的这个方法,此时我们可以用 MagicMock() 来模拟一个方法,之后用 return_value来表示这个方法的返回值,如果返回值也是一个方法,比如是一个get()方法,就可以用 return_value.get.return_value=MagicMock()的写法。 在@patch("requests.Session")的装饰器里,patch方法,会默认就将 requests.Session 创建一个 MagicMock(),然后在执行这个装饰器下面函数的时候,会将这个创建的 MagicMock()作为第一个参数,传给下面的装饰器函数,比如我下面的例子,则会自动传给 mock_session。而mock_response本身就是 pytest.fixture指定的一个 MagicMock(),其有两个属性,一个是 text,一个是status_code。我们在 mock_session_instance.get.return_value = mock_response的这行代码,就将 mock_session 的 get()方法(即 requests.Session().get()方法)和 mock_response进行了绑定
conftest.py
整个项目中所有需要使用的 fixture 固件,都会放到 conftest.py 文件里,pytest会自动引用,无需手动指定
使用parametrize进行参数化测试
示例一
当需要执行pytest,有多个mock的数据,希望对于这些数据进行测试,可以用 parametrize 来做,比如:
注意:上述示例中, parametrize 会将 messages 列表里的每一个元素,在每一次测试的时候都传给 msg,下面的 test_print 函数里的参数,也必须叫 msg才行。
在真实的使用场景中,有可能 messages 列表是某一个方法的返回值,此时我们结合 unittest.mock 的 patch方法,将这个 messages 列表,作为方法的返回值,然后 test_print()里,就能用这个mock的返回值了
示例二
比如我的代码是这样的,此时如果对每一个场景都单独写一个test函数,就会很麻烦,比如年纪大于18写一个,小于18写一个,年龄格式不对的多种场景(输入的是文本,年龄是负数,是小数等),那么要写太多的test_is_audit函数,会很麻烦
此时我们可以用 pytest.mark.parametrize 来进行参数化设置。在pytest.mark.parametrize里,第一个参数里存的是要给测试函数传的参数,如果有多个,用逗号分隔。第二个参数是一个list,里面存的是第一个参数对应的值,每一个list代表了一组值,可以测试多组。 由于上面的测试,包含函数成功返回,和异常抛出错误,所以我们分成两个test function进行测试.
Pycharm debug
在 debug python代码的时候,程序会卡在断点的那一样的位置,那一行的代码处于未被执行的状态。pycharm 对查看断点有几个按钮:
step over: 直接执行这一行的代码
step into:进入到这一行代码内部看具体执行过程
Step into my code:跳过系统包和第三方包,只看自己的代码的执行过程。由IDE判断哪些包是第三方。
step out:在 step into 的过程中,将 step into的剩余部分一次性执行完。step into + step out 相当于 step over
最后更新于