当前位置:网站首页>Pytest testing framework -- data driven
Pytest testing framework -- data driven
2022-07-07 05:18:00 【WenBeacon】
Extract the reason : Since the original text has introduced data-driven in detail , No modification required , So copy the whole article
Purpose of excerpt : In order to practice in the future , It's faster 、 It's more convenient to find what you don't understand , Consolidate and learn
The following is the original content :
introduction
I have already introduced it to you Unittest Test the data-driven framework of the framework DDT, And how it works . What I share with you today is Pytest Data driven testing framework ,Pytest The data-driven test framework is made up of pytest Self contained pytest.mark.parametrize() To achieve .
pytest.mark.parametrize Data driven
pytest.mark.parametrize yes pytest Built in decorator , It allows you to function perhaps class Define multiple sets of parameters and fixture To achieve data-driven .
@pytest.mark.parametrize() The decorator receives two parameters :
The first parameter exists as a string , It represents the parameters that can be accepted by the tested function , If the function under test has multiple parameters , Comma separated ;
The second parameter is used to save the test data . If there is only one set of data , Exist as a list , If there are multiple sets of data , Exist in the form of list nested tuples ( for example :[1,1] perhaps [(1,1), (2,2)]).
Single parameter and multi parameter for decorators , Examples are as follows .
1.pytest.mark.parametrize Single parameter
# test_singal.py
import pytest
@pytest.mark.parametrize("number", [1, 0])
def test_equal(number):
assert number == 1
if __name__ == "__main__":
pytest.main([])
The above is an example of a single parameter , In this case ,test_equal The function takes a parameter number, This parameter has two sets of data , Namely 1 and 0.
tips:
Decorator pytest.mark.parametrize The parameter name in the first parameter of must be consistent with the parameter name in the test function .
namely :test_equal Parameters of this function method number Must match the name of the first parameter in the decorator number bring into correspondence with .
Run the above code , The results are shown in the following figure :

You can see , function test_equal Two sets of parameters are provided 1 and 0, So it also performs 2 Time .
2.pytest.mark.parametrize multi-parameter
pytest.mark.parametrize Not only single parameters are supported , Multiple parameters can also be supported , Multiple parameters are common , Because in daily work , We provide test data , It doesn't just include data for testing , It also includes data for validation , So multi parameter is quite common .
pytest.mark.parametrize It can support multiple parameters , Examples are as follows :
# test_baidu.py
import time
import pytest
from selenium import webdriver
@pytest.mark.baidu
class TestBaidu:
def setup_method(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(30)
self.base_url = "http://www.baidu.com/"
@pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
def test_baidu_search(self, search_string, expect_string):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_id("kw").send_keys(search_string)
driver.find_element_by_id("su").click()
time.sleep(2)
search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
assert (expect_string in search_results) is True
def teardown_method(self):
self.driver.quit()
if __name__ == "__main__":
pytest.main(["-m", "baidu", "-s", "-v", "-k", "test_baidu_search", "test_baidu.py"])
The above code , Tested function test_baidu_search There are two parameters , Namely search_string and expect_string. So it corresponds to , stay pytest.mark.parametrize The first parameter of this decorator , Also contains search_string and expect_string.
The result of running in the command line is as follows :
pytest.fixture Extended data drive
A little partner who has done automated testing , We should all know clearly , No matter what API still UI The automated testing of can be summarized into three steps :
Preparation before test —> Perform the test —> Cleaning after test .
In daily tests , Preparation before testing is usually the prerequisite for testing , It can be a simple login operation 、 Joint query database operation 、 Test data reading preparation operation , Even logically complex function operations .
and unittest Frame like , stay pytest You can also use setup and teardown To complete the pre test work .
for example :
Use setup_method、setup_class、setup_module To complete the test class methods respectively 、 Test class , And test module Of Prepare to operate ;
Use teardown_method、teardown_class、teardown_module To complete the test class methods respectively 、 Test class , And test module Cleanup operations .
But there is an obvious defect in this way .
for example , In the same test class , There are multiple test methods , Suppose that each test method needs different setup perhaps teardown function , What to do at this time ?
And such as ,setup and teardown In fact, they all belong to the test fixture (Test Fixtures), If I want to put all test fixtures into one function to manage , Can you do that? ?
pytest With this in mind , And it provides a more advanced function , That's it fixture Decorator .
fixtures It can be used as an initialization test service 、 Data and status , It is also often used to perform pre - or post test operations before or after test execution .
fixtures It can be used as shared data , It can also be used by other functions 、 modular 、 Class or the whole project , Even other fixtures call .
1.fixtures grammar
pytest.fixtures The grammar is as follows :
fixture(scope="function", params=None, autouse=False, ids=None, name=None)
From the grammar we can see fixture Of 5 The parameters are as follows :
scope: Used to control the fixture The scope of action of
This parameter has the following 4 A level :
function: In every one of them function Or class methods will call ( Default ).
class: Call only once in each class .
module: every last .py File call once ; There can be more than one... In this file function and class.
session: One session Call once .
params: An optional parameter list
params Exists in the form of an optional parameter list . When used in test functions , It can be done by request.param Receive the return value of the setting ( namely params Values in the list ).params How many elements are there in , At testing time , Reference this fixture The function of will be called several times .
autouse: Whether to automatically execute the set fixtures
When autouse by True when , Even if the test function does not call fixture Decorator , Defined fixture The function will also be executed .
ids: Specify each string id
When there is more than one params when , For each param, You can specify id, This id Will become part of the test case name . If not provided id, be id Will be generated automatically .
name:fixture The name of
name yes fixtures The name of , It defaults to the one you decorate fixture Name of function . You can go through name Parameter to change this fixture name , After the change , If this fixture Called , Then use the name you changed .
2.fixtures usage
fixtures There are many ways of using , Examples are as follows .
(1)、 adopt fixture Function names are used directly
#test_fixture_usage.py
import pytest
# First , stay fixture On the function , Add @pytest.fixture()
@pytest.fixture()
def my_method():
print('This is testing fixture')
# secondly , hold fixture The function name of the function is used as the parameter , Pass in the tested case
def test_use_fixtures(my_method):
print('Please follow Testing from WL')
adopt fixture The function name uses fixture The step is :
stay fixture On the function , Add @pytest.fixture(), In the above example my_method This method will serve as fixture Use ;
hold fixture The function name of the function is used as the parameter , Pass in the tested case .
Be careful : function test_use_fixtures The input parameter of must be my_method This method name , Follow fixture The functions are consistent .
By running the above code , In the running results , You'll find that ,my_method It's defined fixture The method of begins to execute before other statements of the test function ( amount to setup function ).
(2)、 adopt usefixtures Decorator use
Through the fixture How to enter parameters as a test function , You can configure different for each test function setup and teardown The function of , But it makes fixture Coupled with my test function , It is not conducive to the reuse of test functions and the clear architecture of the test framework .
therefore pytest Provides pytest.mark.usefixtures This decorator .
The following code illustrates usefixtures Specific usage of :
#test_fixture_usage.py
import pytest
@pytest.fixture()
def my_method():
print('This is Testing fixture')
# The function directly uses fixture
@pytest.mark.usefixtures('my_method')
def test_use_fixtures():
print('Please follow Testing from WL')
class TestClass1:
# Class methods use fixture
@pytest.mark.usefixtures('my_method')
def test_class_method_usage(self):
print('[classMethod]Please follow Testing from WL')
# Class directly uses fixture
@pytest.mark.usefixtures('my_method')
class TestClass2:
def test_method_usage_01(self):
pass
def test_method_usage_02(self):
pass
From this code, you can see ,usefixtures It can be used as a function 、 Class method , And class calls .
(3)、fixture Multi parameter usage
The above use method enables different test functions to call different tests fixtures, So if we fixture What should I do with parameters ? Please look at the following code :
import pytest
@pytest.fixture(params=['hello', 'Testing'])
def my_method(request):
return request.param
def test_use_fixtures_01(my_method):
print('this is the first test')
print(my_method)
@pytest.mark.usefixtures('my_method')
def test_use_fixtures_02():
print('this is the second test')
# Be careful , If you want to pass here print(my_mthod) To print out fixuture Provided parameters , You can't , Because use usefixtures Can't get fixture The return value of , If needed fixture The return value of , You need test_use_fixtures_01 That way
Execute this code , There will be 4 Test cases are executed . thus it can be seen ,pytest adopt fixture And its parameters params Data driven .
(4)、 adopt autouse Parameters implicitly use
The above method realizes fixtures Loose coupling with test function , But there are still problems : Each test function needs to explicitly declare which one to use fixtures.
Based on this ,pytest Provides autouse Parameters , Allow us not to call fixture In the case of decorators, use defined fixture, Please see the following example :
#test_fixture_usage.py
import pytest
@pytest.fixture(params=['hello', 'Testing'], autouse=True, ids=['test1', 'test2'], name='test')
def my_method(request):
print(request.param)
def test_use_fixtures_01():
print('this is the first test')
def test_use_fixtures_02():
print('this is the second test')
By running the above code , And use allure[allure How to generate test report tweet links ] The results of generating the test report are as follows :

When defined fixture function , also autouse by True when , There is no need to explicitly declare in the test function to use fixture( In this case , You can't see it. my_method This fixture Explicitly called in the test method ). Defined fixture Will be in pytest.fixtures Within the specified range , Apply to every test function under it fixture.
In this case ,scope The parameter is undefined , The default value... Will be used “function”, That is, every test function will execute , And ours params Two more sets of parameters are provided , So we have 4 Test cases are executed .
Please note the name of the test case , For each test case , Because in @pytest.fixture It specifies ids by ['test1', 'test2'], Therefore, the test example name also includes the specified id.
(5)、 many fixture Cartesian product uses
When you have more than one fixture When superposition is needed , Can be superimposed . Be careful : This method will put fixure The parameters of each group are organized in the form of Cartesian product , Take the following code as an example , Execution will generate 4 Test cases .
import pytest
class TestClass:
@pytest.fixture(params=['hello', 'Testing'], autouse=True)
def my_method1(self, request):
print('the param are:{}'.format(request.param))
return request.param
@pytest.fixture(params=['world', 'is good'], autouse=True)
def my_method2(self, request):
print('the param are:{}'.format(request.param))
return request.param
def test_use_fixtures_01(self):
pass
(6)、 Use conftest.py To share fixture
Learn from the examples above , You should know how to do it in the same file fixture The definition of 、 Share and use . But in daily work tests , We often need to use the same test pre operation in the global scope .
for example : Log in at the beginning of the test , Then connect to the database and other operations .
In this case , We need to use it conftest.py. stay conftest.py As defined in fixture There is no need for import,pytest Will automatically find and use .pytest lookup fixture The order of is to find the test class first (Class), Then find the test module (Module), And then there was conftest.py file , Finally, there are built-in or third-party plug-ins .
Let's see how to use conftest.py
Suppose the directory structure is as follows :
|--APITest
|--tests
|--test_fixture1.py
|--test_baidu_fixture_sample.py
|--conftest.py
|--__init__.py
conftest.py The code for is as follows :
# conftest.py
import pytest
import requests
from selenium import webdriver
@pytest.fixture(scope="session")
# This method name can be the business code you log in , It could be something else , It is temporarily named login
def login():
driver = webdriver.Chrome()
driver.implicitly_wait(30)
base_url = "http://www.baidu.com/"
s = requests.Session()
yield driver, s, base_url
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()
@pytest.fixture(scope="function", autouse=True)
def connect_db():
print('connecting db')
# Write your connection here db Business logic of
pass
test_fixture1.py The code for is as follows :
# test_fixture1.py
import pytest
class TestClass:
def test_use_fixtures_01(self, login):
print('I am data:{}'.format(login))
test_baidu_fixture_sample.py The code for is as follows :
import time
import pytest
@pytest.mark.baidu
class TestBaidu:
@pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
def test_baidu_search(self, login, search_string, expect_string):
driver, s, base_url = login
driver.get(base_url + "/")
driver.find_element_by_id("kw").send_keys(search_string)
driver.find_element_by_id("su").click()
time.sleep(2)
search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
print(search_results)
assert (expect_string in search_results) is True
if __name__ == "__main__":
pytest.main([])
Execute the following code on the command line :
D:\Auto\APITest>pytest -s -q --tb=no tests --alluredir=./allure_reports
After the test execution is complete , View the execution results :

You can notice from the above figure ,connecting db This statement was printed three times , Because in conftest.py In the connect_db This fixture Of scope Set to function And autouse The property value of is True. and turn off browser driver,turn off requests driver These two statements are executed only once , Because login This fixture Of scope yes session, So it's in the whole session Only once in .
In addition, please pay attention to fixture login in , There are the following statements :
...
...
yield driver, s, base_url
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()
What does this mean ? stay pytest Of fixture in ,yield Before the keyword statement belongs to set up, and yield The following statements belong to tear down.
So you can see , Why is the following statement executed last :
print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()
pytest.mark.parametrize and pytest.fixture Use a combination of
Through the above explanation, we learned , stay pytest Can be used in pytest.mark.parametrize The decorator performs data-driven testing , have access to pytest.fixture Decorator for testing setup、teardown, as well as fixture Shared tests .
When pytest.mark.parametrize and pytest.fixture Combine , What effect can it have ?
(1)、 Less repetitive code , Realize the global sharing of code
All pre and post test functions can be defined in conftest.py In file , For the whole test , Instead of defining in every test class . This greatly reduces duplication of code , And conftest.py Defined in the project root , Can be applied in the global , Defined in a folder , It can be applied to all test files in this folder .
(2)、 You can make the test focus only on the test itself
The test can only be coded around its own business , In combination with conftest.py And pytest.fixture Can be realized , In a test class , Just include the code that tests itself , There is no need to consider the preparation before the test and the cleaning work after the test .
(3)、 Framework migration is easier
If it is UI automated testing , Can be found in conftest.py Included in the document Web Driver All operations , If it is API test , Can be found in conftest.py Write all interface request operations in the file . So when new projects need to apply automation frameworks , Just change tests The test cases under the folder can be .
pytest.mark.parametrize and pytest.fixture With examples :
# test_sample.py
import pytest
@pytest.fixture()
def is_odd(request):
print('Now the parameter are:--{}\n'.format(request.param))
if int(request.param) % 2 == 0:
return False
else:
return True
@pytest.mark.parametrize("is_odd", [1, 0], indirect=True)
def test_is_odd(is_odd):
if is_odd:
print("is odd number")
else:
print("is not odd number")
if __name__ == "__main__":
pytest.main([])
The above code defines a fixture Method is_odd And a data-driven approach test_is_odd. among ,fixture Method is_odd Judge whether a number is odd ; And the data-driven approach test_is_odd Will provide a set of data , And call is_odd This fixture Judge .
summary
Today's sharing is pytest Test how the framework is data driven , It can be used in combination pytest.mark.parametrize and pytest.fixture Decorator . If you learn pytest.mark.parametrize and pytest.fixture Various uses of , Your testing framework will work well .
边栏推荐
- Leetcode(417)——太平洋大西洋水流问题
- 磁盘监控相关命令
- U++ 游戏类 学习笔记
- Two methods of thread synchronization
- pmp真的有用吗?
- 全链路压测:影子库与影子表之争
- Tencent cloud database public cloud market ranks top 2!
- [opencv] image morphological operation opencv marks the positions of different connected domains
- Ansible中的inventory主機清單(預祝你我有數不盡的鮮花和浪漫)
- LinkedBlockingQueue源码分析-初始化
猜你喜欢
随机推荐
人体传感器好不好用?怎么用?Aqara绿米、小米之间到底买哪个
Salesforce 容器化 ISV 场景下的软件供应链安全落地实践
If you‘re running pod install manually, make sure flutter pub get is executed first.
3.基金的类型
Scheduledexecutorservice timer
Leetcode minimum difference in student scores
Full link voltage test: the dispute between shadow database and shadow table
Operand of null-aware operation ‘!‘ has type ‘SchedulerBinding‘ which excludes null.
[736. LISP syntax parsing]
The most complete learning rate adjustment strategy in history LR_ scheduler
《四》表单
U++ 游戏类 学习笔记
vector和类拷贝构造函数
最全常用高数公式
app内嵌h5---iphone软键盘遮挡输入文字
Harmonyos fourth training
JS 的 try catch finally 中 return 的执行顺序
带你遨游银河系的 10 种分布式数据库
Ansible中的inventory主機清單(預祝你我有數不盡的鮮花和浪漫)
项目经理如何凭借NPDP证书逆袭?看这里