How to distinguish the two , It's simple unittest As an official testing framework , More basic in testing , And it can carry out secondary development on the basis of again , At the same time, the format will be more complex in usage ; and pytest Framework as a third-party framework , The convenience lies in more flexible use , And be able to change the original unittest Style test cases have good compatibility , At the same time, it is richer in expansion , Use scenarios can be added through extended plug-ins , For example, some concurrent tests ;
pip install :
pip install pytest Copy code
Test installation successful :
pytest --help py.test --help Copy code
Check the installed version :
pytest --version Copy code
Pytest Writing rules :
test_example.py
def count_num(a: list) -> int: return len(a) def test_count(): assert count_num([1, 2, 3]) != 3 Copy code
Perform the test :
pytest test_example.py Copy code
Execution results :
C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest>pytest test_example.py -v ================================================================= test session starts ================================================================= platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- d:\coding\python3.6\python.exe cachedir: .pytest_cache rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 collected 1 item test_example.py::test_count FAILED [100%] ====================================================================== FAILURES ======================================================================= _____________________________________________________________________ test_count ______________________________________________________________________ def test_count(): > assert count_num([1, 2, 3]) != 3 E assert 3 != 3 E + where 3 = count_num([1, 2, 3]) test_example.py:11: AssertionError =============================================================== short test summary info =============================================================== FAILED test_example.py::test_count - assert 3 != 3 ================================================================== 1 failed in 0.16s ================================================================== Copy code
remarks :
By default ,pytest Will look in the current directory to test_ Start with ( With _test ending ) Test files for , And execute all in the file test_ Start with ( With _test For the end ) All functions and methods of ;
pytest test_example3.py::test_odd Copy code
pytest -k example Copy code
import pytest test_flag = False @pytest.mark.skip() def test_odd(): num = random.randint(0, 100) assert num % 2 == 1 @pytest.mark.skipif(test_flag is False, reason="test_flag is False") def test_even(): num = random.randint(0, 1000) assert num % 2 == 0 Copy code
def test_zero(): num = 0 with pytest.raises(ZeroDivisionError) as e: num = 1/0 exc_msg = e.value.args[0] print(exc_msg) assert num == 0 Copy code
@pytest.mark.xfail() def test_sum(): random_list = [random.randint(0, 100) for x in range(10)] num = sum(random_list) assert num < 20 Copy code
@pytest.mark.parametrize('num,num2', [(1,2),(3,4)]) def test_many_odd(num: int, num2: int): assert num % 2 == 1 assert num2 % 2 == 0 Copy code
Firmware is a preprocessing function ,pytest Before executing the test function ( Or after execution ) Load and run these firmware , Common application scenarios include database connection and closing ( Device connection and shutdown )
Easy to use
import pytest @pytest.fixture() def postcode(): return "hello" def test_count(postcode): assert postcode == "hello" Copy code
According to the official explanation, when running the test function , The parameters of the running function are detected first , Search for parameters with the same name fixture, once pytest find , Will run these firmware , Get the return values of these firmware ( If there is ), And pass these return values as parameters to the test function ;
Next, further verify the official statement :
import pytest @pytest.fixture() def connect_db(): print("Connect Database in .......") yield print("Close Database out .......") def read_database(key: str): p_info = { "name": "zhangsan", "address": "China Guangzhou", "age": 99 } return p_info[key] def test_count(connect_db): assert read_database("name") == "zhangsan" Copy code
Execute test function results :
============================= test session starts ============================= platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir: .pytest_cache rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 collecting ... collected 1 item test_example.py::test_count Connect Database in ....... PASSED [100%]Close Database out ....... ============================== 1 passed in 0.07s ============================== Copy code
remarks :
We have a general understanding of the function of firmware from the front , Extract some repetitive work for reuse , meanwhile pytest In order to control firmware more finely in the framework , Scope is used to specify the scope of firmware ,( For example, the first mock exam function in this module can be executed once. , The function in the module does not need to be executed repeatedly ) A more specific example is the database connection , The operation of this connection may be time-consuming , I only need the first mock exam function to run once. , You don't need to run every time .
And defining firmware is , Usually by scop Parameter to declare the action , Commonly used :
import pytest @pytest.fixture(scope="function") def func_scope(): print("func_scope") @pytest.fixture(scope="module") def mod_scope(): print("mod_scope") @pytest.fixture(scope="session") def sess_scope(): print("session_scope") def test_scope(sess_scope, mod_scope, func_scope): pass def test_scope2(sess_scope, mod_scope, func_scope): pass Copy code
Execution results :
============================= test session starts ============================= platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir: .pytest_cache rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 collecting ... collected 2 items test_example2.py::test_scope session_scope mod_scope func_scope PASSED [ 50%] test_example2.py::test_scope2 func_scope PASSED [100%] ============================== 2 passed in 0.07s ============================== Copy code
You can see it here module,session The scope's firmware was executed only once , You can verify the official introduction
Someone might say , Why is it so troublesome ,unittest Directly defined in the framework setUp Preprocessing can be performed automatically , alike pytest The framework has similar automatic execution ; pytest The firmware in the framework is generally through parameters autouse Control automatic operation .
import pytest @pytest.fixture(scope='session', autouse=True) def connect_db(): print("Connect Database in .......") yield print("Close Database out .......") def test1(): print("test1") def test2(): print("test") Copy code
Execution results :
============================= test session starts ============================= platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir: .pytest_cache rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 collecting ... collected 2 items test_example.py::test1 Connect Database in ....... PASSED [ 50%]test1 test_example.py::test2 PASSED [100%]test Close Database out ....... ============================== 2 passed in 0.07s ============================== Copy code
It turns out that , The test function is automatically executed before and after running connect_db The firmware ;
I mentioned it briefly earlier @pytest.mark.parametrize Pass parametric test , For firmware parameters, you need to pass pytest Firmware built into the frame request, And through request.param To obtain parameters
import pytest @pytest.fixture(params=[ ('redis', '6379'), ('elasticsearch', '9200') ]) def param(request): return request.param @pytest.fixture(autouse=True) def db(param): print('\nSucceed to connect %s:%s' % param) yield print('\nSucceed to close %s:%s' % param) def test_api(): assert 1 == 1 Copy code
Execution results :
============================= test session starts ============================= platform win32 -- Python 3.6.8, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir: .pytest_cache rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 collecting ... collected 2 items test_example.py::test_api[param0] Succeed to connect redis:6379 PASSED [ 50%] Succeed to close redis:6379 test_example.py::test_api[param1] Succeed to connect elasticsearch:9200 PASSED [100%] Succeed to close elasticsearch:9200 ============================== 2 passed in 0.07s ============================== Copy code
Analog connection here redis and elasticsearch, Load the firmware to automatically perform the connection, then execute the test function and disconnect .
Why should we learn about automated testing for development , It is very important to save some time of repetitive work through automated testing , At the same time, for optimizing the code structure , Improve code coverage , And subsequent project reconstruction are of great significance , At the same time understand pytest and unittest What are the differences on this basis? It is helpful to select suitable test tools in different business scenarios . This article is just a brief introduction pytest Basic use of , If you are interested, please go to the official documents , The use of built-in firmware is also mentioned in the official documents , Common test scenarios, etc .
Reference resources :
docs.pytest.org/en/6.2.x/co…learning-pytest.readthedocs.io/zh/latest/d…