An easy guide to PyTest

Pytest is a unit testing framework that helps you write better programs. Flask, Werkzeug, Gunicorn, etc. Learn how to use the PyTest framework.

  • Introduction to the use case
  • Advanced skills
  • summary

Introduction to the use case

Simple use case

Write a simple example of test_sample.py:

# cat test_sample.py
def inc(x):
    return x + 1

def test_answer():
    assert inc(3) == 5
Copy the code

The two methods defined in the example are very simple. Test_answer asserts the target function inc. Run the test case with pytest test_sample.py:

# pytest test_sample.py ========================================================================================================= test session starts ========================================================================================================== Platform Darwin -- Python 3.7.5, PyTest-6.0.2, py-1.9.0, pluggy-0.13.1 rootdir: /Users/yoo/codes/ft6work/devops-py/tests plugins: The parallel - 0.1.0 from xdist - 2.1.0, Forked -1.3.0 collected 1 item test_sample.py F [100%] =============================================================================================================== FAILURES = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = _____________________________________________________________________________________________________________ test_answer ______________________________________________________________________________________________________________ def test_answer(): print("test_answer") > assert inc(3) == 5 E assert 4 == 5 E + where 4 = inc(3) test_sample.py:7: AssertionError ---------------------------------------------------------------------------------------------------------  Captured stdout call --------------------------------------------------------------------------------------------------------- test_answer ======================================================================================================= short test summary info ======================================================================================================== FAILED test_sample.py::test_answer - assert 4 == 5 ========================================================================================================== 1 failed in 0.04 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

If pyTest is not already installed, use PIP Install PyTest to install it.

The test results show that a test case was run, and the result is red, indicating failure. The error message shows that an AssertionError is thrown at line 7 of the code. You can change the code to make the test case green.

This test case involves three simple rules of PyTest:

  • Test module totest_The prefix name
  • The same goes for test cases (functions)test_The prefix name
  • Result judgment useassertAssertions can be

Exception handling

Pytest supports the capture of exceptions. Use with + pytest. Raises to catch exceptions to the target function:

# cat test_exception.py
import pytest

def f():
    raise SystemExit(1)


def test_mytest():
    with pytest.raises(SystemExit):
        f()
Copy the code

The test class

Pytest supports test classes that can be used to group test cases:

# cat test_class.py
class TestClass:
    def test_one(self):
        x = "this"
        assert "h" in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, "check")
Copy the code
  • Classes use the Test prefix, and no additional inheritance is required to Test classes.

Automatic test

Our directory has the following three test modules:

# ll
-rw-r--r--  1 yoo  staff   168B Jul 22 21:32 test_class.py
-rw-r--r--  1 yoo  staff   117B Jul 22 21:30 test_exception.py
-rw-r--r--  1 yoo  staff    73B Jul 22 21:09 test_sample.py
Copy the code

Running PyTest – with no extra parameters – automatically tests 3 modules, very conveniently:

# pytest ============================================================================================== test session starts =============================================================================================== platform darwin -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 rootdir: /Users/yoo/work/yuanmahui/python/ch22-pytest collected 4 items test_class.py .. [ 50%] test_exception.py . [ 75%] test_sample.py . [100%] = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 4 passed in 0.02 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Advanced skills

After understanding the basic use of PyTest, we’ll learn some advanced techniques that will help us write test cases better.

parametrize

Parametrize reduces the writing of test cases. Consider the following test case:

def test_eval():
    assert eval("3+5") == 8
    assert eval("'2'+'4'") == "24"
    assert eval("6*9") == 54
Copy the code

There are many test conditions for eval, and adding use cases requires adjusting functions. This code is not concise and easy to maintain. Use parametrize to optimize this problem:

@pytest.mark.parametrize("test_input,expected", [
    ("3+5", 8),
    ("'2'+'4'", "24"),
    ("6*9", 54)
])
def test_eval_1(test_input, expected):
    assert eval(test_input) == expected
Copy the code

Adjust the parameters of the test function to inputs and expectations, and then set parameter values with parametrize. The function parameter assignment will be performed automatically at runtime. To add a test condition, you don’t need to change the body of test_eval_1.

mark

Mark is a tag that can be configured as follows:

@pytest.mark.slow def test_mark(): print("test mark") # Simulate running a long test case time.sleep(10) assert 5 == 5Copy the code

Add the pytest.ini file to the pytest directory and configure pyTest:

# cat pytest.ini
[pytest]
markers =
    slow: marks tests as slow (deselect with '-m "not slow"')
Copy the code

Use the following command to skip the marked functions and speed up the test:

 pytest test_sample.py -m "not slow"
Copy the code

You can also just run the tagged function

pytest -m slow
Copy the code

fixture

Fixtures can provide functionality like initialization and mock, and here’s a full example:

import pytest # Arrange @pytest.fixture def first_entry(): return "a" # Arrange @pytest.fixture def second_entry(): return 2 # Arrange @pytest.fixture def order(first_entry, second_entry): return [first_entry, second_entry] # Arrange @pytest.fixture def expected_list(): Return ["a", 2, 3.0] def test_string(order, Expected_list): # Expected_list # Assert Assert order == Expected_listCopy the code

You can see that the order parameter of test_String and the EXPECTED_list are both the result of the function flagged earlier using the Pytest.fixture decorator. Fixtures can also be nested, with order nesting first_entry and second_entry.

Now that you know how fixtures work, you might wonder, what’s the point of writing this? For example, a use case to test database write requires a database connection parameter:

def test_database_insert_record(database_connection):
    if database_connection:
        print("Insertion Successful. ", database_connection)
        ...
Copy the code

Using fixtures to solve this problem, write Conftest.py in the test cases directory:

# cat conftest.py @pytest.fixture def database_connection(): # mock... .Copy the code

plugin&&hook

Plugins and hooks can be written to extend PyTest.

Create conftest.py and test-sub.py in a directory:

# cat a/conftest.py

def pytest_runtest_setup(item):
        # called for running each test in 'a' directory
        print("setting up", item)
        
# cat a/test_sub.py

def test_sub():
    pass

Copy the code

Using pytest a/test_sub.py –capture=no will load the plugin and hook we wrote.

. a/test_sub.py setting up <Function test_sub>Copy the code
  • Use system hooks topytest_Is the prefix, namedruntest_setup

You can use Pytest_runtest_setup to implement similar functionality to setup in the test framework.

summary

Pytest is a simple and easy to use test case used in flask, Werkzeug, Gunicorn and other projects. The use method is as follows:

  • The test directory is generally usedtestsName at the same level as SRC
  • Test module usagetest_The prefix
  • Test class usesTestPrefix, do not need to inherit from other superclasses
  • Test cases are also usedtest_The prefix
  • You can parameterize with parametrize
  • You can use Mark to tag test cases
  • You can use fixtures to simulate test conditions
  • Configure PyTest using the pytest.ini file
  • You can write plug-ins and HOO extensions to PyTest

There are many more uses for Pytest, and we’ll learn more about that next time.

Refer to the link

  • Pytest document docs.pytest.org/en/6.2.x/