Fixture is introduced

Fixtures are shell functions that PyTest executes before and after test functions run. The code in fixtures can be customized to meet varying testing requirements, including defining data sets in incoming tests, configuring the initial state of the system before testing, providing data sources for bulk testing, and so on.

import pytest

@pytest.fixture()
def some_data():
    return 42

def test_some_data(some_data):
    assert some_data == 42
Copy the code

The @pytest.fixture() decorator is used to declare that a function is a fixture. If the test function’s argument list contains a fixture name, PyTest will detect it and execute that fixture before the test function runs. Fixtures can perform tasks and also return data to test functions.

Fixtures are shared through conftest.py

If you want multiple test file-sharing fixtures, you can create a confTest.py file in a public directory and place fixtures in it. Conftest. py is treated by PyTest as a native plug-in library. You can think of tests/conftest.py as a fixture repository for all the tests in the tests directory.

Fixtures are used to perform configuration and destruction logic

import pytest
import tasks
from tasks import Task

@pytest.fixture()
def tasks_db(tmpdir):
    """connect to db defore tests, disconnect after."""
    # Setup : start db
    tasks.start_tasks_db(str(tmpdir), 'tiny')
    yield # this is where the testing happens
    # Teardown : stop db
    tasks.stop_tasks_db()
Copy the code

The fixture function runs before the test function, but if the fixture function contains yield, the system stops at the yield, runs the test function, and then returns to the fixture to continue executing the code behind yield. Therefore, code before yield can be treated as a setup process and code after yield as a teardown process. No matter what happens during the test, the code after yield is executed.

Use –setup-show to trace the execution of fixtures

Use the –setup-show option to see what is being executed during the test and in what order. The F and S precedes the fixture name to indicate the scope of fixtures, with F for function level scope and S for session level scope.

(ly_venv) liuyan@Lydemacpro func % pytest --setup-show test_add.py -k valid_id ============================= test Session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform Darwin - Python 3.6.4 radar echoes captured, pytest - 6.2.2, py - 1.10.0, Pluggy - 0.13.1 rootdir: / Users/liuyan PycharmProjects/code/ch3 / a/tasks_proj/tests, configfile: pytest. Ini plugins: Allure - pytest - 2.8.34, HTML - 3.1.1, Metadata-1.11.0 COLLECTED 3 items / 2 deselected / 1 selected test_add.py SETUP S tmp_path_factory SETUP F tmp_path (fixtures used: tmp_path_factory) SETUP F tmpdir (fixtures used: tmp_path) SETUP F tasks_db (fixtures used: tmpdir) func/test_add.py::test_add_returns_valid_id (fixtures used: request, tasks_db, tmp_path, tmp_path_factory, tmpdir). TEARDOWN F tasks_db TEARDOWN F tmpdir TEARDOWN F tmp_path TEARDOWN S tmp_path_factoryCopy the code

Use fixtures to pass test data

Fixtures are great for storing test data, and they can return any data.

import pytest

@pytest.fixture()
def a_tuple():
    """Return something more interesting."""
    return (1, 'foo', None, {'bar': 23})


def test_a_tuple(a_tuple):
    """Demo the a_tuple fixture."""
    assert a_tuple[3]['bar'] == 32
Copy the code

If you assume that an assert exception (or any type of exception) occurs in a fixture, the report will use ERROR instead of FAIL. If the test results FAIL, the user knows that the failure occurred in the core test function, not in the fixture that the test depends on. You can use multiple fixtures.

The advantage of using fixtures is that you can write test functions with only the core test logic in mind, not the preparation

Specify the scope of the fixture

Fixtures contain an optional parameter called scope, which controls how often fixtures perform configuration and destruction logic. The scope argument to @pytest.fixture() has four optional values: function, class, module, and session (function by default).

  • Scope =’function’ : function-level fixtures need to be run only once for each test function. The configuration code runs before the test case runs, and the destruction code runs after the test case runs.
  • Scope =’class’ : class-level fixtures need to be run only once per test class. This fixture can be shared regardless of how many class methods there are in the test class.
  • Scope =’module’ : Module-level fixtures need to be run only once per module. This fixture can be shared regardless of how many class methods there are in the module.
  • Scope =’session’ : Session-level fixtures need to be run only once per session. All the test functions and methods in a PyTest session can share this fixture.

Use usefixtures to specify the fixture

We can mark a test function or class with @pytest.mark.usefixtures(‘fixture1′,’fixture2’). Using usefixtures, you need to specify one or more fixture strings in the parameter list. This doesn’t make much sense for test functions, but it’s great for test classes.

@pytest.mark.usefixtures('class_scope')
class TestSomething():
    """Demo class scope fixtures."""

    def test_3(self):
        """Test using a class scope fixture."""

    def test_4(self):
        """Again, multiple tests are more fun."""
Copy the code

Using usefixtures and adding fixture parameters to the test method are roughly the same. One difference is that only the latter can use the return value of fixtures.

Rename the fixture

Pytest allows you to rename fixtures using the name argument of @pytest.fixture().

import pytest


@pytest.fixture(name='lue')
def ultimate_answer_to_life_the_universe_and_everything():
    """Return ultimate answer."""
    return 42


def test_everything(lue):
    """Use the shorter name."""
    assert lue == 42
Copy the code