1. 什么是fixture
Fixture是Pytest中的一个重要概念,继承自unittest包,它代表测试用例执行前需要准备的环境和数据,在测试用例执行后需要清理的环境和数据。Fixture可以在多个测试用例之间共享,简化了测试代码的编写。
Fixture的使用流程一般如下:
定义fixture -> 使用fixture -> 销毁fixture
2. fixture的定义
在Pytest中,fixture可以分为函数级别和会话级别。函数级别的fixture作用域为函数内部,每个测试用例会执行一次fixture;会话级别的fixture作用域为整个测试过程,测试过程中会执行一次fixture。
2.1 使用函数定义fixture
使用函数定义fixture时,可以使用@pytest.fixture装饰器修饰函数。修饰的函数表示一个fixture,可以在测试函数中作为参数直接调用。
下面是一个简单的例子:
# 定义一个简单的fixture
@pytest.fixture()
def input_data():
data = [1, 2, 3, 4, 5]
return data
# 测试函数中使用fixture
def test_fixture(input_data):
assert len(input_data) == 5
在上述代码中,使用@pytest.fixture修饰的函数input_data表示一个fixture,返回值为一个数组;测试函数中的input_data参数表示调用该fixture得到的返回值。
在Pytest中,默认fixture的作用域为“function”,所以对于函数级别的fixture,一般不需要在fixture函数的装饰器中指定作用域。
2.2 使用类定义fixture
当多个fixture不是独立的,而是有依赖关系时,可以使用类定义fixture。
使用类定义fixture时,需要在类中定义两个方法,一个是用于创建对象的__init__方法,另一个是用于销毁对象的__del__方法,同时还需要使用@pytest.fixture装饰器修饰类方法,表示该方法是一个fixture。
下面是一个简单的例子:
import pytest
class FixtureTest:
def __init__(self):
self.data = [1, 2, 3, 4, 5]
self.start = 1
self.end = 5
def __del__(self):
self.data = None
self.start = None
self.end = None
@pytest.fixture(scope="class")
def input_data(self):
return self.data
@pytest.fixture(scope="class")
def range_data(self):
return range(self.start, self.end)
class TestClass:
def test_fixture(self, input_data, range_data):
assert len(input_data) == 5
assert range_data == range(1, 5)
在上述代码中,FixtureTest类有两个fixture:input_data和range_data。TestClass类中的test_fixture函数可以接受两个fixture作为参数,分别是input_data和range_data。
3. fixture的应用
3.1 fixture的参数化
在定义fixture时,可以使用@pytest.mark.parametrize装饰器为fixture参数化。
下面是一个简单的例子:
@pytest.fixture(params=[1, 2, 3, 4, 5])
def input_data(request):
data = request.param
return data
def test_fixture(input_data):
assert input_data in [1, 2, 3, 4, 5]
在上述代码中,使用@pytest.fixture装饰器修饰的fixture input_data被params参数化为[1, 2, 3, 4, 5],测试函数test_fixture使用input_data作为参数。
3.2 fixture的函数间共享
在Pytest中,fixture可以被定义为shareable,即可以在多个测试函数中共享fixture。
下面是一个简单的例子:
@pytest.fixture(scope="module")
def input_data():
data = [1, 2, 3, 4, 5, 6]
return data
def test_fixture1(input_data):
assert 1 in input_data
assert 2 in input_data
def test_fixture2(input_data):
assert 5 in input_data
assert 6 in input_data
在上述代码中,fixture input_data的作用域为“module”,即整个测试过程中只会执行一次fixture函数,且可以被测试函数test_fixture1和test_fixture2共享。
3.3 fixture的重用
在Pytest中,fixture也可以被其他fixture重用。
下面是一个简单的例子:
@pytest.fixture(scope="module")
def input_data():
data = [1, 2, 3, 4, 5, 6]
return data
@pytest.fixture
def subset_input_data(input_data):
subset = input_data[:3]
return subset
def test_fixture1(input_data):
assert 1 in input_data
assert 2 in input_data
assert 3 in input_data
def test_fixture2(subset_input_data):
assert len(subset_input_data) == 3
assert subset_input_data == [1, 2, 3]
在上述代码中,fixture input_data的作用域为“module”,fixture subset_input_data的作用域为“function”。fixture subset_input_data依赖于fixture input_data,即在调用subset_input_data时,会先调用input_data并获取其返回值。在测试函数test_fixture2中使用subset_input_data作为参数。
4. fixture的高级应用
4.1 yield的使用
在Pytest中,fixture函数可以使用yield语句,表示测试用例执行到yield语句时,停止执行,等到测试用例执行完毕后,再执行yield语句之后的代码。
下面是一个简单的例子:
@pytest.fixture(scope="function")
def input_data():
data = [1, 2, 3, 4, 5]
yield data
data.clear()
def test_fixture(input_data):
assert len(input_data) == 5
在上述代码中,fixture函数input_data使用yield语句来控制fixtures的生命周期。在测试函数test_fixture中调用input_data后,如果断言失败会抛出错误,继而进入yield语句后的代码;如果断言成功,则会继续执行yield语句之后的代码。在上述例子中,yield语句之后清空了data的值。
4.2 自定义fixture
除了可以使用Pytest内置的fixture,还可以自定义fixture。
下面是一个简单的例子:
import pytest
@pytest.fixture(scope="module")
def input_data():
data = [1, 2, 3, 4, 5]
return data
def test_custom_fixture(input_data):
assert len(input_data) == 5
# 自定义fixture
@pytest.fixture(scope="module")
def multiply_input_data(input_data):
multiply_data = [i * 2 for i in input_data]
return multiply_data
def test_multiply_input_data(multiply_input_data):
assert len(multiply_input_data) == 5
assert multiply_input_data == [2, 4, 6, 8, 10]
在上述代码中,自定义了multiply_input_data fixture。在测试函数test_multiply_input_data中使用了该fixture,其中multiply_input_data依赖于input_data fixture。
4.3 使用conftest.py文件
在Pytest中,可以将fixture单独定义在conftest.py文件中。当运行测试用例时,Pytest会自动寻找conftest.py文件,并执行其中的fixture。
下面是一个简单的例子:
# conftest.py文件中定义了fixture
import pytest
@pytest.fixture(scope="function")
def input_data():
data = [1, 2, 3, 4, 5]
return data
# 测试用例共享conftest.py文件中定义的fixture
def test_conftest_fixture(input_data):
assert len(input_data) == 5
在上述代码中,定义了fixture input_data并放在conftest.py文件中。测试用例test_conftest_fixture共享了该fixture。
4.4 代码复用
在Pytest中,fixture的代码复用性很强,可以减少测试代码的重复编写。
下面是一个简单的例子:
@pytest.fixture
def input_list():
return [1, 2, 3, 4, 5]
@pytest.fixture
def input_dict():
return {"a": 1, "b": 2, "c": 3}
def test_fixture(input_list, input_dict):
assert input_list[1] == 2
assert input_dict["c"] == 3
在上述代码中,使用了两个fixture并在测试函数中共享。这样不仅可以减少测试代码的重复编写,还可以增加测试代码的可维护性。
结论
本文主要介绍了Pytest中fixture的使用,包括fixture的定义、应用和高级应用等。 fixture是Pytest中一个非常实用的功能,可以大大简化测试代码的编写和维护。