以下命令可以列出所有可用的fixture,包括内置的、插件中的、以及当前项目定义的。
pytest --fixtures
fixture作为函数参数
测试用例可以接受一个fixture
函数作为参数(函数命名),fixture
函数的注册通过@pytest. fixture
来标记,下面看一个简单的例子:
# test_sample.py
import pytest
@pytest.fixture
def data():
return [1, 2, 3]
def test_equal(data):
print data[0]
assert data[0] == 1
test_equal
需要data
的值,pytest
将会查找并调用@pytest. fixture
标记的data
函数,运行将会看到如下结果:
共享fixture函数
上面的一个例子fixture函数只运用于单个py
文件,如果想要共享数据函数怎么办呢?只需要将这些fixture
函数放入到 conftest.py
文件中即可(名称不可变);在当前路径创建 conftest.py
文件,输入以下代码:
# conftest.py
import pytest
@pytest.fixture
def data():
return [1, 2, 3]
test_sample.py
更新代码:
# test_sample.py
import pytest
def test_equal(data):
print data[0]
assert data[0] == 1
运行代码将会得到同样的结果;如果此时在test_sample.py
文件中也包含fixture标记过的data函数(返回数据改变),那么将会重载该数据(即会调用当前文件中的data)
pytest.fixture
特性实现pytest
中的setup/teardown
经典的xunit风格的setUp、tearDown参考:上篇博客和官网介绍。
pytest.fixture
特性实现:
这里的scope支持 function, class, module, package or session四种,默认情况下的scope是function。
默认使用return的时候是没有tearDown的,但是可以使用yield 、addfinalizer函数来实现tearDown功能。
具体请参考:
tests
│-- conftest.py
│-- test_sample.py
│-- test_sample1.py
conftest.py
代码:
import pytest
pytest.fixture(scope="module")
def try_module():
print "#######################module begin####################"
yield [6, 7, 8]
print "#######################module end####################"
@pytest.fixture() # 不加(scope="function")默认scope就是function
def try_function():
print "#######################function begin####################"
yield "just a test about function scope"
print "#######################function end####################"
@pytest.fixture(scope="class")
def try_class(request):
print "#######################class begin####################"
def end():
print "#######################class end####################"
request.addfinalizer(end)
return "just a test about class scope"
@pytest.fixture(scope="package") # session == package
def try_session(request):
print "#######################session begin####################"
def end():
print "#######################session end####################"
request.addfinalizer(end)
return "just a test about session scope"
test_sample.py
代码:
class TestAdd(object):
def test1(self, try_session):
print "test1 something"
assert 4 == 4
def test2(self, try_module):
print "test2 something"
assert 5 == 5
def test3(self, try_class):
print "test3 something"
assert 5 == 5
def test4(self, try_function):
print "test4 something"
assert 5 == 5
def test5(self):
print "test5 something"
assert 5 == 5
test_sample1.py
代码:
def test_file2_1(try_module):
print "test_file2_1 something"
assert 2 == 2
def test_file2_2(try_function):
print "test_file2_2 something"
assert "a"*3 == "aaa"
def test_file2_3(try_session):
print "test_file2_3 something"
assert "a"*3 == "aaa"
def test_file2_4():
print "test_file2_4 something"
assert "a"*3 == "aaa"
def test_file2_5(try_function):
print "test_file2_5 something"
assert "a"*3 == "aaa"
命令行运行:pytest -v -s -q
运行结果:
============================= test session starts =============================
platform win32 -- Python 2.7.15, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: C:\diy\old coding\test\locust demo\pytest
collected 10 items
test_sample.py #######################session begin####################
test1 something
.#######################module begin####################
test2 something
.#######################class begin####################
test3 something
.#######################function begin####################
test4 something
.#######################function end####################
test5 something
.#######################class end####################
#######################module end####################
test_sample1.py #######################module begin####################
test_file2_1 something
.#######################function begin####################
test_file2_2 something
.#######################function end####################
test_file2_3 something
.test_file2_4 something
.#######################function begin####################
test_file2_2 something
.#######################function end####################
#######################module end####################
#######################session end####################
========================== 10 passed in 0.16 seconds ==========================
简单应用:
import time
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from utils.log import logger
from utils.config import get_url
@pytest.fixture()
def chrome_driver(scope="function"):
print("setup() begin")
driver = webdriver.Chrome()
driver.get(get_url())
print("setup() end")
yield driver
#test_函数结束后,会回到这里,关闭浏览器
print("teardown() begin")
driver.close()
print("teardown() end")
class TestBaiDu(object):
locator_kw = (By.ID, 'kw')
locator_su = (By.ID, 'su')
locator_result = (By.XPATH, '//div[contains(@class, "result")]/h3/a')
def test_search_0(self, chrome_driver):
chrome_driver.find_element(*self.locator_kw).send_keys(u'selenium 测试')
chrome_driver.find_element(*self.locator_su).click()
time.sleep(2)
links = chrome_driver.find_elements(*self.locator_result)
for link in links:
logger.info(link.text)
调用fixture的三种方式
usefixtures
decorator调用(pytest.mark.usefixtures()
标记用例)autouse
调用usefixtures
decorator调用例子:
test_fixture
│-- conftest.py
│-- test_example.py
conftest.py
代码:
import pytest
@pytest.fixture() # 不加(scope="function")默认scope就是function
def before_function():
print "#######################function begin####################"
yield "just a test about function scope"
print "#######################function end####################"
@pytest.fixture(scope="class")
def before_class(request):
print "#######################class begin####################"
def end():
print "#######################class end####################"
request.addfinalizer(end)
return "just a test about class scope"
test_example.py
代码:
import pytest
@pytest.mark.usefixtures("before_function")
def test_1():
print('test_1()')
@pytest.mark.usefixtures("before_function")
def test_2():
print('test_2()')
class TestExample(object):
@pytest.mark.usefixtures("before_function")
def test_1(self):
print('test_1()')
@pytest.mark.usefixtures("before_function")
def test_2(self):
print('test_2()')
@pytest.mark.usefixtures("before_function")
@pytest.mark.usefixtures("before_class")
class TestExample1(object):
# @pytest.mark.usefixtures("before_class")
def test_1(self):
print('test_1()')
def test_2(self):
print('test_2()')
运行pytest -v -s test_example.py
输出结果:
============================= test session starts =============================
platform win32 -- Python 2.7.15, pytest-4.5.0, py-1.8.0, pluggy-0.11.0 -- c:\python27\python.exe
cachedir: .pytest_cache
rootdir: C:\diy\old coding\test\locust demo\pytest
collected 6 items
test_fixture/test_example.py::test_1 #######################function begin####################
test_1()
PASSED#######################function end####################
test_fixture/test_example.py::test_2 #######################function begin####################
test_2()
PASSED#######################function end####################
test_fixture/test_example.py::TestExample::test_1 #######################function begin####################
test_1()
PASSED#######################function end####################
test_fixture/test_example.py::TestExample::test_2 #######################function begin####################
test_2()
PASSED#######################function end####################
test_fixture/test_example.py::TestExample1::test_1 #######################class begin####################
#######################function begin####################
test_1()
PASSED#######################function end####################
test_fixture/test_example.py::TestExample1::test_2 #######################function begin####################
test_2()
PASSED#######################function end####################
#######################class end####################
========================== 6 passed in 0.19 seconds ===========================
autouse
调用例子:
当管理用例比较多的时候,这种方法比较方便高效,但是用该功能时也要小心,一定要注意fixture的作用范围。需要注意的是,当使用这种方式时,就不能使用返回值的功了。autouse
默认设置为False。当默认为False,就可以选择用上面两种方式来试用fixture。当设置为True时,所有的test都会自动调用这个fixture。autouse
遵循scope="关键字参数"
规则:当scope="session"
时,无论怎样定义只运行一次;当scope="module"
时,每个py文件只运行一次;当scope="class"
时,每个class
只运行一次(但是一个文件中包括function和class时,会在每个function(不在class中)运行一次);当scope="function"
时,每个function运行一次;
目录结构:
test_fixture
│-- conftest.py
│-- test_autouse.py
conftest.py
代码:
# coding=utf-8
import pytest
@pytest.fixture(scope="module", autouse=True)
def before_module():
print "#######################module begin####################"
yield [6, 7, 8]
print "#######################module end####################"
@pytest.fixture(autouse=True) # 不加(scope="function")默认scope就是function
def before_function():
print "#######################function begin####################"
yield "just a test about function scope"
print "#######################function end####################"
@pytest.fixture(scope="class",autouse=True)
def before_class(request):
print "#######################class begin####################"
def end():
print "#######################class end####################"
request.addfinalizer(end)
return "just a test about class scope"
@pytest.fixture(scope="package",autouse=True) # session == package
def before_package(request):
print "#######################session begin####################"
def end():
print "#######################session end####################"
request.addfinalizer(end)
return "just a test about session scope"
test_autouse.py
代码:
def test_1():
print('test_1()')
def test_2():
print('test_2()')
class TestExample(object):
def test_1(self):
print('test_1()')
def test_2(self):
print('test_2()')
运行pytest -v -s test_autouse.py
输出结果:
============================= test session starts =============================
platform win32 -- Python 2.7.15, pytest-4.5.0, py-1.8.0, pluggy-0.11.0 -- c:\python27\python.exe
cachedir: .pytest_cache
rootdir: C:\diy\old coding\test\locust demo\pytest
collected 4 items
test_fixture/test_autouse.py::test_1 #######################session begin####################
#######################module begin####################
#######################class begin####################
#######################function begin####################
test_1()
PASSED#######################function end####################
#######################class end####################
test_fixture/test_autouse.py::test_2 #######################class begin####################
#######################function begin####################
test_2()
PASSED#######################function end####################
#######################class end####################
test_fixture/test_autouse.py::TestExample::test_1 #######################class begin####################
#######################function begin####################
test_1()
PASSED#######################function end####################
test_fixture/test_autouse.py::TestExample::test_2 #######################function begin####################
test_2()
PASSED#######################function end####################
#######################class end####################
#######################module end####################
#######################session end####################
========================== 4 passed in 0.11 seconds ===========================
fixture中使用fixture(test_something.py
):
import pytest
@pytest.fixture(scope="module")
def foo(request):
print('\nfoo setup - module fixture')
def fin():
print('foo teardown - module fixture')
request.addfinalizer(fin)
@pytest.fixture()
def bar(request, foo):
print('bar setup - function fixture')
def fin():
print('bar teardown - function fixture')
request.addfinalizer(fin)
@pytest.fixture()
def baz(request, bar):
print('baz setup - function fixture')
def fin():
print('baz teardown - function fixture')
request.addfinalizer(fin)
def test_one(baz):
print('in test_one()')
def test_two(bar): # only use bar
print('in test_two()')
运行py.test -s test_something.py
,输出:
============================= test session starts =============================
platform win32 -- Python 2.7.2 -- pytest-2.4.2
collected 2 items
test_modular.py
foo setup - module fixture
bar setup - function fixture
baz setup - function fixture
in test_one()
.baz teardown - function fixture
bar teardown - function fixture
bar setup - function fixture
in test_two()
.bar teardown - function fixture
foo teardown - module fixture
========================== 2 passed in 0.02 seconds ===========================
参数化
利用fixture的params参数进行参数化:
@pytest.fixture(params=[0, 1, 3])
def c_set(request):
temp = request.param
yield temp
def test_c(c_set):
assert c_set == 3
上面例子运行了三个测试用例,运行结果:
============================= test session starts =============================
platform win32 -- Python 2.7.15, pytest-4.5.0, py-1.8.0, pluggy-0.11.0 -- c:\python27\python.exe
cachedir: .pytest_cache
rootdir: C:\diy\old coding\test\locust demo\pytest
collected 3 items
test_parametrizing.py::test_c[0] FAILED
test_parametrizing.py::test_c[1] FAILED
test_parametrizing.py::test_c[3] PASSED
================================== FAILURES ===================================
__________________________________ test_c[0] __________________________________
c_set = 0
def test_c(c_set):
> assert c_set == 3
E assert 0 == 3
E -0
E +3
test_parametrizing.py:50: AssertionError
__________________________________ test_c[1] __________________________________
c_set = 1
def test_c(c_set):
> assert c_set == 3
E assert 1 == 3
E -1
E +3
test_parametrizing.py:50: AssertionError
===================== 2 failed, 1 passed in 0.18 seconds ======================
也可以利用ids
参数对每个测试定制一个名称标志,将上边函数加一个ids
参数,可以用–collect-only参数来查看(pytest --collect-only test_parametrizing.py
),添加的ids参数可以用-k来过滤运行指定参数的方法(pytest -k "apple" test_parametrizing.py
只会运行参数params=[0, 1, 3]
中值为1的,运行一次),代码如下:
@pytest.fixture(params=[0, 1, 3], ids=["apple","banana","orange"])
def c_set(request):
temp = request.param
yield temp
def test_c(c_set):
assert c_set == 3
也可以为ids传递函数作为参数:
def idfn(fixture_value):
if fixture_value == 0:
return "eggs"
else:
return None
@pytest.fixture(params=[0, 1], ids=idfn)
def d(request):
return request.param
def test_d(d):
pass
使用pytest.param()
,参数值2将被跳过:
import pytest
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
def e_set(request):
return request.param
def test_e(e_set):
pass
利用@pytest.mark.parametrize
直接对测试函数进行参数化:
import pytest
def add(a, b):
return a + b
@pytest.mark.parametrize("test_input, expected", [
([1, 1], 2),
([2, 2], 4),
([0, 1], 1),
])
def test_add(test_input, expected):
assert expected == add(test_input[0], test_input[1])
不同级别的重写fixture
conftest.py
)重写fixture文件夹级别(conftest.py
)重写fixture
tests/
__init__.py
conftest.py # content of tests/conftest.py
import pytest
@pytest.fixture
def username():
return 'username'
test_something.py # content of tests/test_something.py
def test_username(username):
assert username == 'username'
subfolder/ __init__.py
conftest.py # content of tests/subfolder/conftest.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-' + username
test_something.py # content of tests/subfolder/test_something.py
def test_username(username):
assert username == 'overridden-username'
通过上面的例子可以看到,对于特定的测试文件夹级别,可以覆盖具有相同名称的fixture。注意,重写的fixture可以轻松地访问被重写的base fixture或super fixture
module级别的重写fixture
tests/
__init__.py
conftest.py # content of tests/conftest.py
@pytest.fixture
def username():
return 'username'
test_something.py # content of tests/test_something.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-' + username
def test_username(username):
assert username == 'overridden-username'
test_something_else.py # content of tests/test_something_else.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-else-' + username
def test_username(username):
assert username == 'overridden-else-username'
在上面的示例中,在模块级别,相同名称的fixture可以被重写
直接利用参数重写fixture
tests/ __init__.py
conftest.py # content of tests/conftest.py
import pytest
@pytest.fixture
def username():
return 'username'
@pytest.fixture
def other_username(username):
return 'other-' + username
test_something.py # content of tests/test_something.py
import pytest
@pytest.mark.parametrize('username', ['directly-overridden-username'])
def test_username(username):
assert username == 'directly-overridden-username'
@pytest.mark.parametrize('username', ['directly-overridden-username-other'])
def test_username_other(other_username):
assert other_username == 'other-directly-overridden-username-other'
在上面的例子中,fixture值被test参数值覆盖。请注意,即使测试没有直接使用fixture(在函数原型中没有提到),fixture的值也可以通过这种方式被覆盖