写点什么

软件测试 / 测试开发全日制|Pytest 中 yield 的用法详解

  • 2024-01-12
    北京
  • 本文字数:2629 字

    阅读完需:约 9 分钟

前言

在之前的介绍中,我们已经介绍了 fixture 的简单用法,但其实 fixture 还提供了两种非常优雅高效的写法,来完成测试执行前的处理操作与执行后的处理操作,即使用yieldaddfinalizer来实现。本文我们将介绍使用 yield 来实现操作。

yield

在 fixture 中的关键字 yield 主要有两个作用:


  • yield代替return进行参数的传递

  • 起到代码的分割作用,yield之前的代码为setup的作用,yield之后的代码为teardown的作用


yield 与 return


在 pytest 的 fixture 函数中可以使用 yield 代替 return 进行返回,示例如下:


import pytest @pytest.fixture(autouse=True)def fixture_one():    print("执行fixture_one")    yield 1    def test_e(fixture_one):    print("执行test_e")    print(fixture_one)    assert fixture_one == 1  if __name__ == '__main__':    pytest.main(["-s"])
----------------执行结果如下:test_case_4.py::test_e 执行fixture_onePASSED [100%]执行test_e1 ============================== 1 passed in 0.12s ==============================
复制代码


从运行结果我们能看到fixture_one会返回1并传递给test_e,与return的作用完全一致。但如果仅仅只是这样使用的话,毫无意义,因为使用 return 足够了。所以,在实际的使用过程中我们一般会在yield后面加上teardown的代码。


yield 与 teardown


yield 不进行参数传递


对于不需要在前置操作中返回数据的 fixture 函数,加入yield,那么yield之前的代码为用例执行之前的操作(即setup),yield之后的代码为用例执行之后的操作(即teardown)。示例如下:


import pytest @pytest.fixture()def fixture_demo():    # setup    print("\n连接数据库")    yield    # teardown    print("清空脏数据") def test_case(fixture_demo):    print("执行test_case")    assert True  if __name__ == '__main__':    pytest.main(["-s"])
--------------------运行结果如下:============================= test session starts =============================collecting ... collected 1 item
test_demo01.py::test_case 连接数据库PASSED [100%]执行test_case清空脏数据

============================== 1 passed in 0.02s ==============================
复制代码


从结果中我们可以看出来,先执行了setup部分,再执行测试用例,最后执行teardown部分。

yield 进行参数传递

yield 可以将参数传递给测试用例。


假设有这样一个场景,需要用到接口1的返回参数作为接口2的请求参数,即接口2依赖接口1,我们需要写一条测试用例对接口2进行测试,这个时候可以将接口 1 的请求写在前置中,如果是unittest框架则代码如下:


import unittestimport requests class TestDemo(unittest.TestCase):     def setup(self):        print("请求接口1")        self.res_1 = requests.get(url=url_1, params=params_1)     def test_api_2(self):        print("验证接口2")        # 将接口1的返回值self.res_1作为请求参数,请求接口2        res = requests.post(url=url_2, data=self.res_1)        # 断言        self.assertEqual(res, "接口2预期的返回结果")     def teardown(self):        print("清空脏数据")
复制代码


pytest框架中使用fixture+yield则可编写如下:


@pytest.fixture()def get_api_1_result():    # setup    res_1 = requests.get(url=url_1, params=params_1)    yield res_1    # teardown    print("清空脏数据")     def test_api_2(get_api_1_result):    print("验证接口2")    # 将接口1的返回值res_1作为请求参数,请求接口2    res = requests.post(url=url_2, data=get_api_1_result)    # 断言    assert res == "接口2预期的返回结果"
复制代码


其中,fixture 会先通过yield返回res_1,并传入测试用例test_api_2中,test_api_2运行完成后再去执行yield后面的代码,即执行print("清空脏数据")


通过以上对比unittestsetupteardown以及参数的传递,我们就能很直观的看出pytestyield的使用方式,此处代码仅为示例。

yield 的执行顺序

有时候我们会遇到一个 fixture 函数调用另一个或多个 fixture 函数,且这些函数中可能含有 yield,我们先看示例,代码如下:


import pytest @pytest.fixturedef fixture_1():    print("\n执行fixture_1")    yield 1    print("\n执行fixture_1的teardown代码") @pytest.fixturedef fixture_2(fixture_1):    print("\n执行fixture_2")    yield 2    print("\n执行fixture_2的teardown代码") @pytest.fixturedef fixture_add(fixture_1, fixture_2):    print("\n执行fixture_add")    result = fixture_1 + fixture_2    yield result    print("\n执行fixture_add的teardown代码")     def test_demo(fixture_add):    print("\n执行测试函数test_demo")    assert fixture_add == 3  if __name__ == '__main__':    pytest.main(["-s"])
-----------执行结果如下:collecting ... collected 1 item test_case_4.py::test_demo 执行fixture_1 执行fixture_2 执行fixture_addPASSED [100%]执行测试函数test_demo 执行fixture_add的teardown代码 执行fixture_2的teardown代码 执行fixture_1的teardown代码 ============================== 1 passed in 0.12s ==============================
复制代码


从结果可以看出:


test_demo·测试函数执行之前:先执行了 fixture_1,再执行fixture_2,最后执行fixture_add,注意此时都是执行yield之前的的代码;


test_demo 测试函数执行之后:先执行了 fixture_add,再执行fixture_2,最后执行fixture_1,注意此时都是执行yield之后的的代码。


因此,当一个fixture函数调用另一个或多个fixture函数,且fixture函数中含有yield时,被测试函数调用时有如下执行顺序:


  • 测试函数执行之前,pytest 会根据 fixture 函数之间的线性关系顺序调用,即依次执行 yield 之前的代码。

  • 而测试函数执行结束后,pytest 会根据之前的顺序反方向执行 fixture 函数中 yield 之后的代码。

总结

总的来说,yield 关键字为 Pytest fixture 提供了一种优雅的方式来处理资源管理、测试环境设置和清理工作,使得测试代码更加健壮和可靠。希望本文能够帮到大家!


用户头像

社区:ceshiren.com 微信:ceshiren2021 2019-10-23 加入

微信公众号:霍格沃兹测试开发 提供性能测试、自动化测试、测试开发等资料,实时更新一线互联网大厂测试岗位内推需求,共享测试行业动态及资讯,更可零距离接触众多业内大佬。

评论

发布
暂无评论
软件测试/测试开发全日制|Pytest中yield的用法详解_霍格沃兹测试开发学社_InfoQ写作社区