一、概述
介绍pytest框架中的前置和后置之前,先来介绍一个测试场景,比如我们要测试淘宝的购物车的添加功能,那我们是不是需要先登录,然后再到购物车模块去操作,最后是退出账号,那么像这种测试一个模块前后需要做的准备工作和收尾的工作,可以通过写代码实现,但是pytest帮我们提供了两种方法:
1、通过teardown和setup来控制前置和后置执行的位置。
2、通过在conftest.py文件中利用在函数上加@pytest.fixture()装饰器的方法来实现。
作用范围区别:
1是仅在当前的所在的模块中生效。
2是可以在多个.py文件中共享,或者是多个包以及整个测试项目下被共享。
二、通过setup_xx 和 teardown_xx来实现前置和后置
根据用例运行级别可以分为以下几种
模块级(setup_module/teardown_module)开始于模块始末,全局的
函数级(setup_function/teardown_function)只对函数用例生效(不在类中)
类级(setup_class/teardown_class)只在类中前后运行一次(在类中)
方法级(setup_method/teardown_method)开始于方法始末(在类中)
类里面的(setup/teardown)运行在调用方法的前后
举例说明:
一、函数级别的。
import pytest
count = 1
def setup_function():
print("函数测试开始了.....")
def teardown_function():
print("函数测试结束了.....")
def add(x, y):
if type(x) is int or type(y) is int:
return x+y
else:
raise TypeError
# @pytest.mark.ToDo3
def test_case1():
print("正常值。。。")
assert add(1,2) == 3
# @pytest.mark.toDo2
# @pytest.mark.skipif(count <= 1, reason = "count值太小")
def test_case2():
print("异常值")
with pytest.raises(TypeError):
assert add(1,"2") != 3
if __name__ == '__main__':
pytest.main(['-sq', 'test_10_21.py'])
运行结果,可以看到:每个测试函数执行前和执行后都会去执行一遍。
C:\Python36\python.exe D:/自动化B/python_test/test_10_21.py
函数测试开始了.....
正常值。。。
.函数测试结束了.....
函数测试开始了.....
异常值
.函数测试结束了.....
sss
2 passed, 3 skipped in 0.04s
Process finished with exit code 0
二、模块级别的。
将上面的那两行代码改成下面两行:
def setup_module():
print("模块测试开始了.....")
def teardown_module():
print("模块测试结束了.....")
运行结果:
函数测试开始了.....
正常值。。。
.异常值
.sss函数测试结束了.....
2 passed, 3 skipped in 0.06s
可以看到,在整个模块运行前和运行结束后分别执行了一次。
三、类和方法(这里需要注意一下,这个是放在类中的。)
import pytest
count = 1
def setup_module():
print("模块测试开始了.....")
def teardown_module():
print("模块测试结束了.....")
def add(x, y):
if type(x) is int or type(y) is int:
return x+y
else:
raise TypeError
# @pytest.mark.ToDo3
def test_case1():
print("正常值。。。")
assert add(1,2) == 3
# @pytest.mark.toDo2
# @pytest.mark.skipif(count <= 1, reason = "count值太小")
def test_case2():
print("异常值")
with pytest.raises(TypeError):
assert add(1,"2") != 3
# @pytest.mark.skip(reason = "我不想被执行")
class TestCase(object):
def setup_class(self):
print("类测试开始了.....")
def teardown_class(self):
print("类测试结束了.....")
def setup_method(self):
print("方法测试开始了.....")
def teardown_method(self):
print("方法测试结束了.....")
def setup(self):
print("方法调用测试开始了.....")
def teardown(self):
print("方法调用测试结束了.....")
def test_CASE3(self):
print("特殊字符:")
with pytest.raises(TypeError):
assert add("$", "****") != 3
@pytest.mark.skip()
def test_error():
print("这是一个错误结果的用例测试")
assert 1 == 2
@pytest.mark.skip()
def test_CASE4():
print("特殊字符:")
with pytest.raises(TypeError):
assert add("][]", ">?>") != 3
if __name__ == '__main__':
pytest.main(['-sv', 'test_10_21.py'])
运行结果:
模块测试开始了.....
正常值。。。
.异常值
.类测试开始了.....
方法测试开始了.....
方法调用测试开始了.....
特殊字符:
.方法调用测试结束了.....
方法测试结束了.....
类测试结束了.....
ss模块测试结束了.....
3 passed, 2 skipped in 0.07s
三、通过conftest.py文件中添加@pytest.fixture的方式
首先介绍下conftest,py文件吧,conftest.py文件名是固定的命名方式,不可改变,位置可以放在根目录下,也可以放在模块下,放在不同的位置,则在执行测试用例的时候被执行的顺序也是不一样的。放在根目录下,则会在首次执行根目录下的setup.py的时候会被执行一次。然后接着去执行包下的conftest.py(前提是包下面放了conftest.py)
从上面看,正式因为conftest。py文件只要有,每次执行用例的时候会自动加载这个文件,那么是不是跟前置和后置有点像,所以就有人想到了在这个文件中放置@pytest.fixture,通过scope来控制作用的范围:
@pytest.fixture(scope=“function”, params=None, autouse=False, ids=None, name=None)
1. scope参数:
scope:有四个级别参数 "function" (默认), "class", "module" or "session"
scope参数为session,那么所有的测试文件执行前执行一次
scope参数为module,那么每一个测试文件执行前都会执行一次
scope参数为class,那么每一个测试文件中的测试类执行前都会执行一次
scope参数为function,那么每一个测试文件中的每一个测试用例执行前都会执行一次
2. params参数:
默认是None,也可以传参数,传多个值时,则会执行多次测试用例,这样就实现了参数化。
@pytest.fixture(scope="function",params=[1,2])
def data(request):
return request.param
def test_add(data):#会被执行两次,第一次传入的data=1,第二次传入的data=2
assert 1==data
3. autouse参数
默认是False,只有传入参数名称才会被调用,当开启时,则不管有没有将函数作为参数传入到测试用例中,都会被执行。
ids和name很少用,这里不重点介绍了。。。感兴趣可以自行查找相关资料了解。。
使用的方法是在测试用例的类上通过@pytest.mark.usefixture()
或者直接在方法的参数中出传入被@pytest.fixture()装饰的函数名即可。
以上介绍的都是fixture实现前置,那么后置怎么实现呢?
接下来介绍一下yeild关键字,这个应该学过python的都不陌生了吧,生成器中就是使用的这个关键字来实现的,感兴趣的小伙伴可以去查看我这篇文章中有介绍:https://blog.csdn.net/weixin_43726471/article/details/120600966
那yield和return有什么区别呢?
@pytest.fixture(scope="function")
def yied_ex():
print("前置工作") #step1
yield "run...." #step2
print("后置工作")#step4
def test_yield(yied_ex):
print("返回什么{}".format(yied_ex))#step3
运行结果:
test0205.py::test_yield 前置工作
返回什么run....
PASSED后置工作
相同点:
yield和return都是作为函数的返回
区别:
return返回,函数就结束了,并且return后面不能跟代码了,否则会报语法错误。
但是yield后面还是可以跟代码,遇到yield就停止了,返回yield后的数据,然后回到函数调用的地方,执行完之后,会再次回到yield这里,继续执行yield后面的语句。
四、总结
为什么有setup和teardown,还要出现fixture?
像如下这种场景,就比较适合用fixture,setup和teardown无法满足。
一个.py文件中,有三个测试用例,其中测试用例1和测试用例3需要获得登陆的token,测试用例2不需要,那么可以在测试用例1和测试用例3中传入函数名,2不用传,则不会被执行。
@pytest.fixture(scope="function")
def login():
return "token"
def test_01(login):
print("返回什么{}".format(login))
def test_02():
assert 1==1
def test_03(login):
print("返回什么{}".format(login))
运行结果:
test0205.py::test_01 返回什么token
PASSED
test0205.py::test_02 PASSED
test0205.py::test_03 返回什么token
PASSED
如果使用setup来实现的话:
def setup_function():
print("前置准备。。。")
def test_01():
# print("返回什么{}".format(login))
pass
def test_02():
assert 1==1
def test_03():
# print("返回什么{}".format(login))
pass
返回结果:
test0205.py::test_01 前置准备。。。
PASSED
test0205.py::test_02 前置准备。。。
PASSED
test0205.py::test_03 前置准备。。。
PASSED
可以看到全部被执行。