暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

「Pytest」工具美眉之初学fixture(二)

将咖啡转化为程序的工具人 2021-09-09
370

工具美眉在上一篇fixture(一)中简单说了说什么是fixture,如何自定义一个fixture方法来实现测试用例的前置和后置工作。这一篇将重点介绍一下几个常用的pytest自带的fixture。

pytestconfig

pytestconfig 是一个很实用的内置fixture, 可以获取命令行参数、选项、配置文件、插件、运行目录等方式来控制pytest。

例如:获取命令行参数:--env

    def pytest_addoption(parser):
    parser.addoption(
    "--env",
    action="store",
    default="test_env1",
    help="env to setup before test functions",
    )


    @pytest.fixture
    def env(pytestconfig):
    return pytestconfig.getoption("--env")

    通过pytestconfig.getoption("--env")获取用户输入的命令行参数。


    例如:获取pytest.ini配置文件中的配置信息

      [pytest]
      markers=
      web_test
      app_test
      ...

      获取ini配置文件中所有的marker信息

        @pytest.fixture
        def get_ini(pytestconfig):
            print(pytestconfig.getini('markers'))


        request

        request是一个内置的fixture用于向fixture传递参数使用。

        举个例子,大部分的测试用例都会要求先登录,那就需要把登录单独抽出来写个fixture函数。其它用例全部的调用这个登陆函数就行。但是登录的账号,密码不能写死,所以需要对fixture函数通过request传参。

          # 测试账号数据
          test_user_data = [{"user": "admin1", "passwd": "111111"},
          {"user": "admin1", "passwd": ""}]


          @pytest.fixture(scope="module")
          def login(request):
          user = request.param["user"]
          passwd = request.param["passwd"]
          print("登录账户:%s" % user)
          print("登录密码:%s" % passwd)
          if passwd:
          return True
          else:
          return False


          # indirect=True 声明login是个函数
          @pytest.mark.parametrize("login", test_user_data, indirect=True)
          def test_login(login):
          """登录用例"""
              islogin = login
              print("测试用例中login的返回值:%s" % islogin)
              assert islogin, "失败原因:密码为空"

          除此之外,还可以通过request.addfinalizer,实现在case结束后运行代码,跟上一篇中提到的yield有点类似。

            @pytest.fixture()
            def demo_fixture(request):
                print("这个fixture在每个case前执行一次")
            def demo_finalizer():
                    print("在每个case完成后执行的teardown")


            #注册demo_finalizer为终结函数
            request.addfinalizer(demo_finalizer)


            def test_01(demo_fixture):
            print("\n===执行了case: test_01===")


            def test_02(demo_fixture):
            print("\n===执行了case: test_02===")


            def test_03(demo_fixture):
            print("\n===执行了case: test_03===")

            执行效果就是在每个testcase结束之后,都会运行demo_finalizer方法。

            yield和finalizer的区别是什么呢?

            1)通过request.addfinalizer的方法可以注册多个cleanup函数,要注意的是执行顺序,与注册的顺序相反。

            2)如果使用yield之前的setup的代码发生了任何异常,yield之后的代码将不再执行。但是通过request.addfinalizer注册的终结器,即使fixture的setup部分的代码发生了异常,终结器仍然会继续运行。


            cache

            cache 是一个可以在测试会话之间共享的缓存对象。pytest 运行完用例之后会生成一个 .pytest_cache 的缓存文件夹,用于记录用例的ids和上一次失败的用例。

            pytest --lf--last failed
            重跑最后一轮执行的测试用例中失败的case,如果没有失败的,就重跑所有的
            pytest --ff
            --failed first
            执行所有的测试用例,但是先跑上一轮失败的那些测试用例
            pytest --nf
            --new-first
            执行所有的测试用例,但是先跑新文件里的测试用例

            pytest 

            --cache-show


            展示cache的内容

            pytest

            --cache-clear


            清空cache

            可以通过request.config拿到当前测试会话的cache

              # content of test_caching.py
              import pytest
              import time


              def expensive_computation():
              print("running expensive computation...")


              @pytest.fixture
              def mydata(request):
              val = request.config.cache.get("example/value", None)
              if val is None:
              expensive_computation()
              val = 42
              request.config.cache.set("example/value", val)
              return val


              def test_function(mydata):
              assert mydata == 23

              cache提供了两个方法,set和get。

              cache.set(key, value):写入缓存

              • key 参数:缓存存储的路径,是相对于缓存文件 .pytest_cache/v/ 的相对路径,如无此路径会自动创建

              • value 参数:缓存的值

              cache.get(key, default):返回给定键的缓存值,如果尚未缓存任何值或无法读取该值,则返回指定的默认值

              • key 参数:缓存存储的路径,也是相对于缓存文件 .pytest_cache/v/ 的相对路径;

              • default 参数:获取不到缓存值时的默认值


              monkeypatch


              monkeypatch主要是一种pytest的mock test方式,能够动态地在运行时替换一些属性。设想一下以下这种应用场景:你的测试用例中的某个调用特别耗时,且操作复杂。你希望可以mock这个方法,去测试其余的代码是否能够正确执行。比较常见的做法是直接修改这个复杂方法的代码,或者是修改所有调用这个方法的入口。其实你可以并不需要修改太多的代码,利用monkeypatch就可以在运行时,动态将复杂的方法偷梁换柱,变成你的mock方法,看下面这个例子:

                class Calculator:
                @classmethod
                def expensive_calculate(cls):
                # 这是一个执行特别耗时的方法
                return "response"


                def test_case1(monkeypatch):
                def mockreturn():
                response = "abc"
                return response


                    # 通过monkeypatch将expensive_calculate方法动态替换成mockreturn
                    monkeypatch.setattr(Calculator, "expensive_calculate", mockreturn)
                x = Calculator.expensive_calculate()
                    assert x == "abc"


                也可以通过monkeypatch去mock http请求的返回

                  monkeypatch.setattr(requests, "get", mock_get)


                  除了以上这些,pytest还自带了tmp_path这样的创建临时文件的fixture方法,本篇就不展开介绍了。


                  总结一下:pytest自带的好用的fixture

                  • cache ——pytest 缓存,testcase 可以在session级别共享一些信息

                  • monkeypatch——pytest mock工具,可以动态替换object的属性

                  • pytestconfig——可以获取命令行参数、选项、配置文件等

                  • request——用于向fixture传递参数使用,通常与parametrize和indirect=True一起使用


                  今天的学习就到这里,如果这篇文章对你有帮助,请记得点赞和转发哦👍

                  文章转载自将咖啡转化为程序的工具人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论