Pytest框架之fixture的详细使用教程

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中一个非常实用的功能,可以大大简化测试代码的编写和维护。

后端开发标签