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使用

原文地址:https://www.cnblogs.com/my_captain/p/12727176.html