Pytest测试框架(三):pytest fixture 用法

xUnit style 结构的 fixture用于初始化测试函数, pytest fixture是对传统的 xUnit 架构的setup/teardown功能的改进。pytest fixture为测试准备一个良好的测试环境,测试函数使用的每个 fixture通常有一个参数(以 fixture 命名),测试函数通过参数访问它们。本文将介绍pytest fixture的一些基本用法。

@pytest.fixture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pytest

@pytest.fixture()
def login():
print("登录")
return 8

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self, login):
print("\n开始执行测试用例2")
print(login)
assert 2 + login == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100

if __name__ == '__main__':
pytest.main()

test_case2需要调用login方法(或者获取login的返回值),pytest 将会寻找并调用@pytest.fixture标记的login() 方法。

结果:

1
2
3
4
5
6
7
8
PASSED                           [ 33%]
开始执行测试用例1
登录
PASSED [ 66%]
开始执行测试用例2
8
PASSED [100%]
开始执行测试用例3

共享 fixture 函数:conftest.py

在测试过程中,多个测试文件可能都要调用 fixture 函数,可以将其移动到 conftest.py 文件中。conftest.py 文件中的 fixture 函数不需要在测试函数中导入,可以被 pytest 自动识别,查找顺序从测试类开始,然后是测试模块,然后是 conftest.py 文件,最后是内置插件和第三方插件。

conftest.py :

1
2
3
4
5
import pytest
@pytest.fixture()
def login():
print("登录")
return 8

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pytest

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self, login):
print("\n开始执行测试用例2")
print(login)
assert 2 + login == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
PASSED                           [ 33%]
开始执行测试用例1
登录
PASSED [ 66%]
开始执行测试用例2
8
PASSED [100%]
开始执行测试用例3

yield方法

使用yield关键字可以实现setup/teardown的功能,在yield关键字之前的代码在case之前执行,yield之后的代码在case运行结束后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pytest

@pytest.fixture()
def login():
print("登录")
yield
print("退出登录")

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self, login):
print("\n开始执行测试用例2")
assert 2 + 8 == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
9
PASSED                      [ 33%]
开始执行测试用例1
登录
PASSED [ 66%]
开始执行测试用例2
退出登录
PASSED [100%]
开始执行测试用例3

addfinalizer方法

addfinalizer也可以实现环境的清理,实现与yield方法相同的效果,跟yield不同的是需要注册作为终结器使用的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pytest

@pytest.fixture()
def login(request):
print("登录")
def demo_finalizer():
print("退出登录")
# 注册demo_finalizer为终结函数
request.addfinalizer(demo_finalizer)

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self, login):
print("\n开始执行测试用例2")
assert 2 + 8 == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
PASSED               [ 33%]
开始执行测试用例1
登录
PASSED [ 66%]
开始执行测试用例2
退出登录
PASSED [100%]
开始执行测试用例3

fixture 作用范围:Scope

fixture 作用范围可以为module、class、session和function,默认作用域为function。

  • function:每一个函数或方法都会调用
  • class:每一个类调用一次
  • module:每一个.py文件调用一次
  • session:是多个文件调用一次

    scope=”function”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import pytest

    @pytest.fixture(scope="function")
    def login():
    print("登录...")

    class Test_Demo():
    def test_case1(self, login):
    print("\n开始执行测试用例1")
    assert 1 + 1 == 2

    def test_case2(self, login):
    print("\n开始执行测试用例2")
    assert 2 + 8 == 10

    def test_case3(self, login):
    print("\n开始执行测试用例3")
    assert 99 + 1 == 100


    if __name__ == '__main__':
    pytest.main()

结果:

1
2
3
4
5
6
7
8
9
登录...
PASSED [ 33%]
开始执行测试用例1
登录...
PASSED [ 66%]
开始执行测试用例2
登录...
PASSED [100%]
开始执行测试用例3

scope=”class”

一个class里面多个用例都调用了此fixture,那么只在class里所有用例开始前执行一次

1
2
3
4
5
6
import pytest

@pytest.fixture(scope="class")
def login():
print("登录...")

结果:

1
2
3
4
5
6
7
登录...
PASSED [ 33%]
开始执行测试用例1
PASSED [ 66%]
开始执行测试用例2
PASSED [100%]
开始执行测试用例3

fixture自动应用

autouse参数

autouse设置为True时,自动调用fixture功能。由于默认作用域为function,不指定scope则每个方法都会调用fixture方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pytest

@pytest.fixture(autouse=True)
def login():
print("登录...")

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self):
print("\n开始执行测试用例2")
assert 2 + 8 == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
9
登录...
PASSED [ 33%]
开始执行测试用例1
登录...
PASSED [ 66%]
开始执行测试用例2
登录...
PASSED [100%]
开始执行测试用例3

@pytest.mark.usefixtures()

在测试方法上加@pytest.mark.usefixtures()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pytest

@pytest.fixture()
def login():
print("登录...")

@pytest.mark.usefixtures("login")
class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 1 + 1 == 2

def test_case2(self):
print("\n开始执行测试用例2")
assert 2 + 8 == 10

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
9
登录...
PASSED [ 33%]
开始执行测试用例1
登录...
PASSED [ 66%]
开始执行测试用例2
登录...
PASSED [100%]
开始执行测试用例3

fixture函数参数化

如果多条用例都需要调用相同参数,可以将fixture函数参数化。fixture 函数将执行每个参数值,fixture通过固定参数request传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pytest

@pytest.fixture(scope="module", params=[
[1, 1, 2],
[2, 8, 10],
[99, 1, 100]
])
def data(request):
yield request.param

class Test_Demo():
def test_case1(self):
print("\n开始执行测试用例1")
assert 2 + 8 == 10

def test_case2(self, data):
print("\n开始执行测试用例2")
assert data[0] + data[1] == data[2]

def test_case3(self):
print("\n开始执行测试用例3")
assert 99 + 1 == 100


if __name__ == '__main__':
pytest.main()

结果:

1
2
3
4
5
6
7
8
9
10
PASSED                     [ 20%]
开始执行测试用例1
PASSED [ 40%]
开始执行测试用例2
PASSED [ 60%]
开始执行测试用例2
PASSED [ 80%]
开始执行测试用例2
PASSED [100%]
开始执行测试用例3

本文标题:Pytest测试框架(三):pytest fixture 用法

文章作者:hiyo

文章链接:https://hiyongz.github.io/posts/pytest-fixture/

许可协议:本博客文章除特别声明外,均采用CC BY-NC-ND 4.0 许可协议。转载请保留原文链接及作者。

关注微信公众号,及时接收最新技术文章!