Comprehensive analysis of python pytest:
Three ways to run pytest
import pytest
@pytest.mark.finished
def test_add():
print("测试函数:test_add")
@pytest.mark.finished
def test_subtract():
print("测试函数:test_subtract")
@pytest.mark.unfinished
def test_no_finish():
pass
if __name__ == "__main__":
pytest.main(["-s", "pt_test1.py"])
method one
pytest.main(["-s", "pt_test1.py"])
way two
-
Create a new pytest in pycharm
image.png
-
Click to run
image.png
way three
- Execute with command
D:\project\ut>pytest pt_test1.py
======================================================================= test session starts ========================================================================
platform win32 -- Python 3.6.6, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: D:\project\ut
collected 2 items
pt_test1.py .. [100%]
======================================================================== 2 passed in 0.02s =========================================================================
function for testing
::
Make test function
pytest pt_test1.py::test_add
pytest.main(["pt_test1.py::test_add"])
-k
Fuzzy search, test function of fuzzy search add
pytest -k add pt_test1.py
pytest.main(["-s", "pt_test1.py", "-k", "add"])
- After using
pytest.mark
the callout, use-m
the parameter
@pytest.mark.finished
def test_add():
print("测试函数:test_add")
pytest -m finished pt_test1.py
- A function can be tagged with multiple tags; multiple functions can also be tagged with the same tag to run the logic:
pytest -m "finished and commit"
skip test
pytest.mark.skip
# test_skip.py
@pytest.mark.skip(reason='out-of-date api')
def test_connect():
pass
pytest tests/test-function/test_skip.py
pytest.mark.skipif
Specify ignored conditions for test functions
@pytest.mark.skipif(conn.__version__ < '0.2.0',
reason='not supported until v0.2.0')
def test_api():
pass
pytest tests/test-function/test_skip.py
to parameterize
- Test function for password length
# test_parametrize.py
@pytest.mark.parametrize('passwd',
['123456',
'abcdefdfs',
'as52345fasdf4'])
def test_passwd_length(passwd):
assert len(passwd) >= 8
$ pytest tests/test-function/test_parametrize.py
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
rootdir: F:\self-repo\learning-pytest, inifile:
collected 3 items
tests\test-function\test_parametrize.py F.. [100%]
================================== FAILURES ===================================
- Let's look at another example with multiple parameters, which is used to verify user passwords:
# test_parametrize.py
@pytest.mark.parametrize('user, passwd',
[('jack', 'abcdefgh'),
('tom', 'a123456a')])
def test_passwd_md5(user, passwd):
db = {
'jack': 'e8dc4081b13434b45189a720b77b6818',
'tom': '1702a132e769a623c1adb78353fc9503'
}
import hashlib
assert hashlib.md5(passwd.encode()).hexdigest() == db[user]
$ pytest -v tests/test-function/test_parametrize.py::test_passwd_md5_id
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0 -- c:\anaconda3\python.exe
cachedir: .pytest_cache
rootdir: F:\self-repo\learning-pytest, inifile:
collected 2 items
tests/test-function/test_parametrize.py::test_passwd_md5_id[User<Jack>] PASSED [ 50%]
tests/test-function/test_parametrize.py::test_passwd_md5_id[User<Tom>] PASSED [100%]
========================== 2 passed in 0.07 seconds ===========================
firmware
- Firmware (
Fixture
) is some functions,pytest
which will be loaded and run before (or after) the test function is executed
-Pytest
usingpytest.fixture()
the definition of firmware, the following is the simplest firmware, only returns the Beijing zip code
@pytest.fixture()
def postcode():
return '010'
def test_postcode(postcode):
assert postcode == '010'
preprocessing and postprocessing
- In many cases, it is necessary to perform preprocessing (such as creating a new database connection) before the test, and clean up (close the database connection) after the test is completed.
Pytest
Useyield
keywords to divide the firmware into two parts. The code before yield is preprocessing and will be executed before the test; the code after yield is postprocessing and will be executed after the test is completed.
# test_db.py
@pytest.fixture()
def db():
print('Connection successful')
yield
print('Connection closed')
def search_user(user_id):
d = {
'001': 'xiaoming'
}
return d[user_id]
def test_search(db):
assert search_user('001') == 'xiaoming
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
rootdir: F:\self-repo\learning-pytest, inifile:
collected 1 item
tests\fixture\test_db.py Connection successful
.Connection closed
scope
When defining the firmware, scope
declare the scope through parameters, the options are:
function
: Function level, each test function will execute the firmware once; the default scope is functionclass
: Class level, each test class is executed once, and all methods can be used;module
: Module level, each module is executed once, and functions and methods within the module can be used;session
: Session level, a test is executed only once, and all found functions and methods are available.
@pytest.fixture(scope='function')
def func_scope():
pass
@pytest.fixture(scope='module')
def mod_scope():
pass
@pytest.fixture(scope='session')
def sess_scope():
pass
@pytest.fixture(scope='class')
def class_scope():
pass
- For class use scope, you need to use pytest.mark.usefixtures (also works for functions and methods):
# test_scope.py
@pytest.mark.usefixtures('class_scope')
class TestClassScope:
def test_1(self):
pass
def test_2(self):
pass
$ pytest --setup-show tests/fixture/test_scope.py::TestClassScope
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
rootdir: F:\self-repo\learning-pytest, inifile:
collected 2 items
tests\fixture\test_scope.py
SETUP C class_scope
tests/fixture/test_scope.py::TestClassScope::()::test_1 (fixtures used: class_scope).
tests/fixture/test_scope.py::TestClassScope::()::test_2 (fixtures used: class_scope).
TEARDOWN C class_scope
Pass multiple arguments in pytest using command line
- configure conftest.py
# conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--input1", action="store", default="default input1")
parser.addoption("--input2", action="store", default="default input2")
@pytest.fixture
def input1(request):
return request.config.getoption("--input1")
@pytest.fixture
def input2(request):
return request.config.getoption("--input2")
- write test function
# test.py
import pytest
@pytest.mark.unit
def test_print_name(input1, input2):
print ("Displaying input1: %s" % input1)
print("Displaying input2: %s" % input2)
- Excuting an order
>py.test -s test.py --input1 tt --input2 12
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
rootdir: pytest, inifile:
collected 1 item
test.py Displaying input1: tt
Displaying input2: 12
.
============================================== 1 passed in 0.04 seconds ====================================
Summary of some other parameters
-v, --verbose
Detailed results
--q, --quiet
simplified result display, simplified console output, it can be seen that the output information is different from the previous one without adding -q no information. In the figure below, there are two .. dots instead of pass results-input the debugging information in our use case, such as the print information of print, etc., we add a sentence print(driver.title) in the use case, let's run our use case again, debug information output-you can output more detailed execution information of the use case, such as the file where the use case is located and the name of the
use-s
case
,-V
etc.--junit-xml=path
Output xml file format, used when integrating with jenkins--result-log=path
Save the final result to a local file
2020-8-21 Added
- use
setup
,setup_cass
_teardown_class
class TestCase():
def setup(self):
print("setup: 每个用例开始前执行")
def teardown(self):
print("teardown: 每个用例结束后执行")
def setup_class(self):
print("setup_class:所有用例执行之前")
def teardown_class(self):
print("teardown_class:所有用例执行之前")
def setup_method(self):
print("setup_method: 每个用例开始前执行")
def teardown_method(self):
print("teardown_method: 每个用例结束后执行")
def test_one(self):
print("正在执行----test_one")
x = "this"
assert 'h' in x
def test_three(self):
print("正在执行test_two")
a = "hello"
b = "hello word"
assert a in b
def add(self,a, b):
print("这是加减法")
return a + b
if __name__ == '__main__':
pytest.main(['-s', 'test_fixt_class'])
If you have any questions about learning Python, learning methods, learning routes, and how to learn efficiently, you can consult me at any time, or if you lack systematic learning materials, I have been in this industry for a long time, and I think I am quite experienced. I can help you make constructive suggestions. This is my Python communication qun: 785128166.