• fixture的参数化


    背景

    在需要每次都要进程一组设备测试的时候,需要用到fixtures参数化,比如需要对夜神和Honor 5C进行APP自动化测试,这时候参数化就可以起到很好的用途

    这个两台设备实际应用中代码和报告:

    params=["Honor_5C", "YeShen"]
    
    #登录:无toast弹框,不重置
    @pytest.fixture(params=params)
    def login_common_driver(request):
        driver = BaseDriver().base_driver(device=request.param)
        is_welcome(driver)
        yield driver
        driver.close_app()
        driver.quit()

    参数化用法

    使用params 

    request是pytest中内建的fixture之一,代表fixture的调用状态,request.param作为返回值供测试使用。比如下面的例子,简单判断下拿到的request.param值有没有在原来的参数列表中。实际上就相当于遍历了一遍参数列表(参数化变量actual_url可以是列表或元组)们可以看到测试方法被调用了两次

    #test_fixture_param.py
    import pytest
    
    actual_url = ["www.baidu.com", "www.google.com"]
    
    @pytest.fixture(params=actual_url)
    def back_actual_url(request):
        return request.param
    
    def test_url(back_actual_url):
        assert back_actual_url in actual_url

    使用params + ids

    如果没有指定ids,就是上面那个运行结果,额可以看到测试id拿的是参数列表中的元素。如果指定了ids,这个测试id就变成了了指定的值

    #test_fixture_param.py
    import pytest
    
    actual_url = ["www.baidu.com", "www.google.com"]
    
    @pytest.fixture(params=actual_url, ids=["test001", "test002"])
    def back_actual_url(request):
        return request.param
    
    def test_url(back_actual_url):
        assert back_actual_url in actual_url

    ids也可以是一个函数,比如我们根据条件判断,如果默认id为"www.baidu.com",则通过idfunc()函数将"www.baidu.com"转化为"baidu",ids=idfunc用来接收转化后的值,然后指定id后,按照新的id值作为测试id

    #test_fixture_param.py
    import pytest
    
    actual_url = ["www.baidu.com", "www.google.com"]
    
    
    def idfunc(fixture_value):
        if fixture_value == "www.baidu.com":
            return "baidu"
        else:
            return None
    
    @pytest.fixture(params=actual_url, ids=idfunc)
    def back_actual_url(request):
        return request.param
    
    def test_url(back_actual_url):
        assert back_actual_url in actual_url

    参数化之笛卡尔乘积

    如果有下面一种场景,即同一个测试方法同时调用两个fixture,而这两个fixture又各自有两个参数,那么总共有要执行多少次用例?执行结果又如何?

    #test_fixture_param.py
    import pytest
    
    actual_url = ["www.baidu.com", "www.google.com"]
    expect_url = ["www.baidu.com", "www.google.com"]
    
    
    
    @pytest.fixture(params=actual_url)
    def back_actual_url(request):
        return request.param
    
    
    @pytest.fixture(params=expect_url)
    def back_expect_url(request):
        return request.param
    
    
    def test_url(back_actual_url, back_expect_url):
        assert back_actual_url == back_expect_url

    答案是4次,执行结果是两个PASS,两个Fail,这就是笛卡尔乘积:A = (0, 1),B = (0, 1), A X B = { (0, 0), (0, 1) (1, 0), (1, 1) },只有 0 = 0, 1 = 1,其他两种不相等,所以Fail

    fixture实例引起的测试自动分组

    一个参数中的元素可以看做是一个fixture实例,有两个参数可以看做是调用了两次fixture实例。正如最开始提到的多个设备执行测试,有设备列表[夜神,Honor],有其他参数[1, 2],那么在执行必定是夜神-1,夜神-2,Honor-1,荣耀-2。举个更复杂的官方的例子:

    #test_module.py
    import
    pytest @pytest.fixture(scope="module", params=["mod1", "mod2"]) def modarg(request): param = request.param print(" SETUP modarg", param) yield param print(" TEARDOWN modarg", param) @pytest.fixture(scope="function", params=[1, 2]) def otherarg(request): param = request.param print(" SETUP otherarg", param) yield param print(" TEARDOWN otherarg", param) def test_0(otherarg): print(" RUN test0 with otherarg", otherarg) def test_1(modarg): print(" RUN test1 with modarg", modarg) def test_2(otherarg, modarg): print(" RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))


    =========================== test session starts ============================
    platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
    cachedir: $PYTHON_PREFIX/.pytest_cache
    rootdir: $REGENDOC_TMPDIR
    collecting ... collected 8 items
    
    test_module.py::test_0[1]   SETUP otherarg 1
      RUN test0 with otherarg 1
    PASSED  TEARDOWN otherarg 1
    
    test_module.py::test_0[2]   SETUP otherarg 2
      RUN test0 with otherarg 2
    PASSED  TEARDOWN otherarg 2
    
    test_module.py::test_1[mod1]   SETUP modarg mod1
      RUN test1 with modarg mod1
    PASSED
    test_module.py::test_2[mod1-1]   SETUP otherarg 1
      RUN test2 with otherarg 1 and modarg mod1
    PASSED  TEARDOWN otherarg 1
    
    test_module.py::test_2[mod1-2]   SETUP otherarg 2
      RUN test2 with otherarg 2 and modarg mod1
    PASSED  TEARDOWN otherarg 2
    
    test_module.py::test_1[mod2]   TEARDOWN modarg mod1
      SETUP modarg mod2
      RUN test1 with modarg mod2
    PASSED
    test_module.py::test_2[mod2-1]   SETUP otherarg 1
      RUN test2 with otherarg 1 and modarg mod2
    PASSED  TEARDOWN otherarg 1
    
    test_module.py::test_2[mod2-2]   SETUP otherarg 2
      RUN test2 with otherarg 2 and modarg mod2
    PASSED  TEARDOWN otherarg 2
      TEARDOWN modarg mod2
    
    
    ============================ 8 passed in 0.12s =============================

    看到这个结果是不是很疑惑

    画了一个不太准确的图,简单说明下,为什么是这种执行顺序?首先pytest是按照test_0,test_1,test_2的顺序执行的

    想象fixture实例在使用时就相当于占用了一个资源,当test_0执行时,资源先被[1, 2]中的1占用,然后虽然test_3也有用到[1, 2],但没有test_1还没有开始,是不能进入test_3的,所以资源传递给了[1, 2]中的2,同理2执行结束后,就开始了test_1,所以能把资源传递给mod1,当mod1在test_1中执行完后,发现test_2要用到它,于是继续给test_2使用,test_2使用完后,mod1将资源传递给了mod2,这时候mod2交给test_1先使用,最后传递给test_2使用

  • 相关阅读:
    Rest API
    Spring webflux
    混合配置
    Profile
    Bean的作用域
    事件的监听与发布
    Condition
    完美解决 fatal: unable to access ‘https://github.com/.../.git‘: Could not resolve host: github.com
    virtualbox 给centos扩容方法
    Mac 安装mysqlclient报错 OSError: mysql_config not found
  • 原文地址:https://www.cnblogs.com/my_captain/p/12727176.html
Copyright © 2020-2023  润新知