背景
在需要每次都要進程一組設備測試的時候,需要用到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使用