在我们真正的编写测试用例之前,我们需要了解一下测试框架。目前python自带的unittest和第三方测试框架pytest这两个测试框架比较流行,unittest在过去使用的人很多,近两年pytest有逐渐取代unittest之势。今天我们先了解一下这两个测试框架。
unittest
认识unittest
在我们真正的编写测试用例之前,我们需要了解一下测试框架。
unittest是python自带的单元测试框架,尽管其主要是为单元测试服务的,但我们也可以用它来做自动化测试。
unittest框架为我们编写用例提供了如下的能力:
定义用例的能力。unittest框架有一套固有套路,可以让我们定义测试用例时更加简单和统一
断言的能力。unittest框架提供了一系列的断言
各种执行策略。通过test suit或者扩展的方式,我们可以自定义用例执行的策略
用例编写
我们先说一下用例的格式吧,我们先写个测试用例吧:
import unittest
class PrintTest(unittest.TestCase):
def setUp(self):
self.num = 3
print("num0:%s"%self.num)
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
self.assertEqual(self.num,6,"self.num发生改变不等于3")
def test_test2(self):
print("num2:%s"%self.num)
def tearDown(self):
self.num = 0
print("num3:%s"%self.num)
if __name__ == '__main__':
unittest.main()
接下来由这个例子给大家讲一下unittest如何编写测试用例
导入unittest包
import unittest
这个就不用我多说了吧,python语言需要用哪个包或者模块时候一定要先导入才能使用
定义测试类
class PrintTest(unittest.TestCase)
初学者看到这一行就害怕,其实大可不必。暂且把它当作是一个套路吧,测试类的名字你可以随意取,当然了最好遵循python的PEP8规范,这样代码看起来更加整洁美观。所有的测试类都必须直接或间接的继承自unittest.TestCase类。总之,这还是套路,记住就好。
用例初始化数据
def setUp(self):
self.num = 3
print("num0:%s"%self.num)
setUp(self)方法是一个测试用例初始化方法,在每个测试用例执行之前都会执行一次,是做数据初始化的好地方。在上面的例子里,我们为每一个测试方法都定义了被测对象--self.num,通俗一点就是每次执行测试用例时,self.num都会被赋值为3
测试用例
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
self.assertEqual(self.num,3,"self.num发生改变不等于3")
首先,在unittest测试框中,他会把以test开头的函数做位测试用例,换句话说,就是每个测试用例的命名已经做了约束,开头必须是test,只有test开头的才是测试用例
另外,unittest框架自身也有断言,断言是测试用例的一个重要组成部分,断言的设置直接决定着自动化测试用例的效果。这里强调一下,大家一定要养成在断言后加入自己业务相关的信息,方便根据自动化报告分析错误,可以提高发现问题的效率
测试环境恢复
def tearDown(self):
self.num = 0
print("num3:%s"%self.num)
在测试过程中,是需要对测试后的环境和数据进行恢复的,在unittest测试框架中,tearDown(self)这个方法就是用来恢复测试环境的,每个用例执行完都会执行一遍
这里我在补充一点,如果是测试环境需要初始化和恢复,而且不需要每个测试用例都初始化,我们可以用setUpClass()和tearDownClass()这两个类,但是必须使用@classmethod装饰器进行修饰
@classmethod
setUpClass()
self.num=0
这样表示在当前类里所有测试用例执行之前将self.num赋值为0,在用例执行过程中不再执行,适用于初始化测试环境
@classmethod
tearDownClass()
self.num=0
这样表示在当前类里所有测试用例执行完之后将self.num恢复为0,在单个用例执行结束后不再执行,适用于恢复测试环境,这里我强调一下这两个类作用域仅限于当前类里的所有test,不是所有的测试用例py文件
用例执行
if __name__ == '__main__':
unittest.main()
依然是套路,上面的代码表示,如果直接执行该python文件的话,就运行所有的测试类里的测试用例,也就是运行所有的以test开头的方法,我们姑且这么认为,unittest.main()是最简单的用例执行方法
接下来我们将上述代码运行一遍看看结果
num0:3
num1:6
num3:0
num0:3
num2:3
num3:0
Ran 2 tests in 0.000s
OK
从结果我们可以看到,每个测试用例执行顺序是setUp-->test_case-->tearDown
unittest小结
使用unittest的话需要记住下面的几点
导入unittest
定义继承自unittest.TestCase的测试类
定义以test开头的测试方法,这个方法就是测试用例,你可以在一个类里定义n个测试用例
断言后面的信息尽量和自己业务相关,方便定位问题
unittest.main()是执行测试用例最简单的方式
pytest
更完善的pytest
近两年pytest使用的人也越来越多,主要是pytest的扩展性和其他方面的功能要比unittest更完善,最最重要的是,pytest可以兼容unittest,也就是说unittest的测试用例在pytest框架里也能执行,但是要注意的一点是文件名要符合pytest的规则。
pytest用例编写
我们首先说一下pytest的用例编写规则:
测试文件以test_开头(以test结尾也可以)
测试类以Test开头,并且不能带有 init 方法
测试函数以test开头
断言还是继承python的断言assert
现在我们参照这个规则把上面的unittest框架的测试用例用pytest实现
test_print.py
import pytest
class TestPrint:
def setup(self):
self.num = 3
print("num0:%s"%self.num)
def test_test1(self):
self.num= self.num+3
print("num1:%s"%(self.num))
assert self.num==6,"self.num发生改变不等于6"
def test_test2(self):
print("num2:%s"%self.num)
def teardown(self):
self.num = 0
print("num3:%s"%self.num)
if __name__ == '__main__':
pytest.main(['-s', 'test_print.py'])
定义测试类
class TestPrint:
测试类一定要以Test开头,记住,还是套路,小写test或者Test放在类名后面都不行,会加载不到里面的测试用例
测试环境数据初始化与恢复
pytest里面依然是使用setup()和teardown()作为初始化数据和恢复测试环境的方法,只不过是全小写了而已。当然在pytest也存在setup_class()和
teardown_class()这样的类,用来在当前测试类里所有测试用例执行之前初始化和全部执行完后恢复数据和环境,且不需要再使用@classmethod这样的装饰器进行修饰
测试用例执行
pytest在执行测试用例时可以参数化,而且根据不同的需要可以定制化执行结果,我们先简单看几个参数:
pytest.main(['-s', 'test_print.py'])
#output:
collected 2 items
test_print.py num0:3
num1:6
.num3:0
num0:3
num2:3
.num3:0
============================== 2 passed in 0.08s ==============================
可以把print的内容打印出来,并输出执行结果和用时
pytest.main(['-v', 'test_print.py'])
#output:
collecting ... collected 2 items
test_print.py::TestPrint::test_test1 PASSED [ 50%]
test_print.py::TestPrint::test_test2 PASSED [100%]
============================== 2 passed in 0.09s ==============================
这里是不是没有print的内容了,他会展示每条case的执行结果
当然我们还可以组合使用
pytest.main(['-v','-s', 'test_print.py'])
#output:
collecting ... collected 2 items
test_print.py::TestPrint::test_test1 num0:3
num1:6
PASSEDnum3:0
test_print.py::TestPrint::test_test2 num0:3
num2:3
PASSEDnum3:0
============================== 2 passed in 0.06s ==============================
既可以显示print内容还可以看到每条用例的执行结果
常用命令参数
命令行 | 解释 |
---|---|
pytest --version |
显示版本信息 |
pytest --fixtures |
显示可用的内置函数 |
pytest -h --help |
显示参数和配置的帮助信息 |
pytest --lf |
运行上一次运行失败的用例 |
pytest -x --exitfirst |
第一次失败后停止 |
pytest --maxfail=2 |
第二(n)次失败后停止 |
pytest test_mod.py |
运行单个文件中的用例 |
pytest testcase/ |
运行文件夹下的用例 |
pytest -k "MyClass and not method" |
关键字表达式(文件名、类名、方法名)运行测试用例 (将运行TestMyClass.test_something 不运行TestMyClass.test_method_simple) |
pytest test_mod.py::test_func |
运行模块内特指的方法 |
pytest test_mod.py::TestClass::test_method |
运行模块下类内特指的方法 |
pytest -m slow |
marker运行测试用例(运行所有被装饰器标记@pytest.mark.slow的用例) |
pytest --pyargs pkg.testing |
从包中运行测试用例(这将要导入import pkg.testing) |
pytest -ra |
运行测试用例(显示测试总的结果信息,输出信息的最后) |
pytest -rp |
运行测试用例(显E示测试通过的结果信息,输出信息的最后) |
pytest -rE |
运行测试用例(显示测试错误的结果信息,输出信息的最后) |
pytest -rs |
运行测试用例(显示测试跳过的结果信息,输出信息的最后),也可以结合使用 -rfs – 显示跳过、失败的 |
pytest -v pytest1.py |
用于显示每个测试函数的执行结果 |
pytest -q pytest1.py |
只显示整体测试结果 |
pytest -s pytest1.py |
用于显示测试函数中print()函数输出 |
pytest --durations=10 |
获得最慢的10个测试持续时间表 |
pytest --junitxml=path |
生成一个结果集xml文件,可用于Jenkins 持续集成 |
pytest --pastebin=failed |
为每个失败的用例创建一个URL |
pytest小结
关于pytest的使用方法我就先介绍这么多了,关于pytest的其他更高阶的用法,大家可以去查阅pytest使用手册
unittes VS pytest
用例编写
使用unittest编写测试用例必须遵循以下规则:
1.测试类必须要继承 unittest.TestCase
2.测试方法必须以test_kai开头
pyest是Python的第三方测试框架,是基于unittest的扩展框架,比unittest更简洁高效,使用pytest编写测试用例必须遵循以下规则:
1.测试文件必须以test开头或者_test结尾
2.测试方法需以test开头
3.测试类必须以Test开头
前置跟后置
1.unittest提供了setUp/tearDown,每个用例运行前、结束后运行一次。setUpClass和tearDownClass,用例执行前、结束后,只运行一次。
2.pyets 可以在函数前使用@pytest.fixture()装饰器,fixture使用范围可以是:function(函数级别)、class、module(模块级别)、package(包级别)、session(多个测试类可以共用一个session)
优势
1.fixure命名更加灵活,局限性比较小
2.conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置,可供多个py文件调用。
3.scope="session" 以实现多个.py跨文件使用一个session来完成多个用例
断言
1.unittest提供了assertEqual、assertIn、assertTrue、assertFalse等
2.pytest直接在assert 后面接表达式
失败重跑
1.unittest无此功能
2.pytest支持
参数化
1.unittest需要依赖于ddt库
2.pytest直接使用@pytest.mark.parametrize装饰器
扩展性
与unittest相比,pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(完美html测试报告生成)、pytest-xdist(多CPU分发)等