Public account concerns: test charging treasure, communication together

Fixtures parameterized

Fixture functions can be called parameterized, in which case the associated test set is called multiple times, that is, the set of tests that depend on the fixture. Test functions are usually not concerned with this kind of repeated testing.

The parameterization of fixtures helps you write detailed functional tests for components that can be configured in a variety of ways.

Extending the previous example, we marked fixtures to create two instances of smTP_connection, which would cause all the tests to run twice using these two different fixtures:

# conftest.py 
import pytest 
import smtplib 

@pytest.fixture(scope="module", params=["smtp.qq.com"."mail.163.org"]) 
def smtp_connection(request) : 
    smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) 
    yield smtp_connection 
    print("finalizing %s" % smtp_connection) 
    smtp_connection.close()
Copy the code

The main change from the previous code is to define a params for @Pytest. fixture, which is a list that can be accessed in fixtures through Request. params. Without modifying the rest of the code, let’s run it:

(pytest) D:\study\auto-pytest>pytest test_module.py ================================== test session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.7.1, pytest - 6.0.2, py - 1.9.0, pluggy - 0.13.1 rootdir: D:\study\auto-pytest collected 4 items test_module.py FFFF [100%] ================================== FAILURES ================================== __________________________________ test_ehlo[smtp.qq.com0] __________________________________ smtp_connection = <smtplib.SMTP object at 0x0000025BBB5D7EF0> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert b"smtp.qq.com" in msg E AssertionError: assert b'smtp.qq.com' in b'newxmesmtplogicsvrszb5.qq.com\nPIPELINING\nSIZE 73400320\nSTARTTLS\nAUTH LOGIN PLAIN\nAUTH=LOGIN\nMAILCOMPRESS\n8BITMIME' test_module.py:4: AssertionError __________________________________ test_noop[smtp.qq.com0] __________________________________ smtp_connection = <smtplib.SMTP object at 0x0000025BBB5D7EF0> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for debug E assert 0 test_module.py:11: AssertionError __________________________________ test_ehlo[smtp.qq.com1] __________________________________ smtp_connection = <smtplib.SMTP object at 0x0000025BBB5D7EF0> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert b"smtp.qq.com" in msg E AssertionError: assert b'smtp.qq.com' in b'newxmesmtplogicsvrszb5.qq.com\nPIPELINING\nSIZE 73400320\nSTARTTLS\nAUTH LOGIN PLAIN\nAUTH=LOGIN\nMAILCOMPRESS\n8BITMIME' test_module.py:4: AssertionError __________________________________ test_noop[smtp.qq.com1] __________________________________ smtp_connection = <smtplib.SMTP object at 0x0000025BBB5D7EF0> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for debug E assert 0 test_module.py:11: AssertionError --------------------------------- Captured stdout teardown --------------------------------- finalizing <smtplib.SMTP object at 0x0000025BBB5D7EF0> ================================== short test summary info ================================== FAILED test_module.py::test_ehlo[smtp.qq.com0] - AssertionError: assert b'smtp.qq.com' in b'newxmesmtplogicsvrszb5.qq.com\nPIPELINING\nSIZE 73400320\nSTARTTLS\nAUTH LOGIN PLAIN\nAU... FAILED test_module.py::test_noop[smtp.qq.com0] - assert 0 FAILED test_module.py::test_ehlo[smtp.qq.com1] - AssertionError: assert b'smtp.qq.com' in b'newxmesmtplogicsvrszb5.qq.com\nPIPELINING\nSIZE 73400320\nSTARTTLS\nAUTH LOGIN PLAIN\nAU... FAILED test_module. Py: : test_noop [1] smtp.qq.com - assert 0 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 4 FAILED in 0.31 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

You can see that each test function was run twice with a different smTP_Connection instance.

Use Marks in parameterized fixtures

Pytest.param () can be used to receive tokens passed through the marks argument, as with @Pytest.mark.parametrize. As follows:

# test_fixture_marks.py 
import pytest 

@pytest.fixture(params=[0.1, pytest.param(2, marks=pytest.mark.skip)]) 
def data_set(request) : 
    return request.param

def test_data(data_set) : 
    pass
Copy the code

Running this test skips calls with a value of 2 in data_set:

(pytest) D:\study\auto-pytest>pytest test_fixture_marks.py ==================================== test session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.7.1, pytest - 6.0.2, py - 1.9.0, pluggy - 0.13.1 rootdir: D:\study\auto-pytest collected 3 items test_fixture_marks.py .. S [100%] = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2 passed, 1 skipped in 0.14 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Modularity: Use fixtures through the fixture function

Not only can test functions use fixtures, but the fixture functions themselves can use other fixtures. This makes it easier to modularize fixture design and reuse fixtures across multiple projects.

Extending the previous example as a simple example, we insert an instantiated APP object into an already defined smtp_connection:

# test_appsetup.py 

import pytest 

class App(object) : 
    def __init__(self, smtp_connection) : 
        self.smtp_connection = smtp_connection 
        
@pytest.fixture(scope="module") 
def app(smtp_connection) :
	return App(smtp_connection) 

def test_smtp_connection_exists(app) : 
    assert app.smtp_connection
Copy the code

Here we define a fixture named app and take the previously defined SMtp_connection fixture, in which we instantiate an app object. The result is as follows

(pytest) D:\study\auto-pytest>pytest -v test_appsetup.py ======================================== test session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.7.1, pytest - 6.0.2, py - 1.9.0, Plugy-0.13.1 -- d:\envs\pytest\scripts\python.exe cacheDir:. Pytest_cache Rootdir: D:\study\auto-pytest collected 2 items test_appsetup.py::test_smtp_connection_exists[smtp.qq.com0] PASSED [ 50%] test_appsetup.py::test_smtp_connection_exists[smtp.qq.com1] PASSED [100%] ======================================== 2 Passed in 0.17 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Because smTP_Connection is parameterized, the test case will run two different App instances to connect to their respective SMTP servers. App fixtures don’t need to worry about parameterization of smTP_Connection; PyTest automatically analyzes dependencies

Note that app fixtures declare module scoped and use smtp_connection, which is also module scoped. This example would still be valid if smTP_connection were session scoped: Fixtures can refer to more scoped fixtures, but not the other way around, such as session-scoped fixtures that cannot refer to a module-scoped fixture

Rewrite the fixtures

In large projects, you may need to redefine a fixture locally to override a global or root fixture in order to keep the code readable and maintainable.

Overwrite in the folder (ConfTest) layer

The test file structure is as follows:

tests/
	__init__.py 
    conftest.py 
    	# tests/conftest.py 
        import pytest 
        @pytest.fixture 
        def username() : 
            return 'username' 
    
    test_something.py 
    	# test/test_something.py 
    	def test_username(username) : 
            assert username == "username" 
    subfolder/ 
    	__init__.py 
        conftest.py 
        	# tests/subfolder/conftest.py 
            import pytest 
            @pytest.fixture 
            def username(username) : 
                return 'the overridden ‐' + username 
        test_something.py 
            # tests/subfolder/test_something.py 
            def test_username(username) : 
                assert username == 'the overridden ‐ username'
Copy the code

As shown above, fixtures can be rewritten using the same function name.

Rewrite it in the Module layer

The file structure is as follows:

tests/
	__init__.py 
    conftest.py 
    	# tests/conftest.py 
        import pytest 
        @pytest.fixture 
        def username() : 
            return 'username' 
    
    test_something.py 
    	# test/test_something.py 
        import pytest
        
        @pytest.fixture
        def username(username) :
            return 'the overridden ‐' + username
        
    	def test_username(username) : 
            assert username == "username" 
    test_something_else.py
    	# tests/test_something_else.py
        import pytest
        @pytest.fixture
        def username(username) :
            return 'the overridden ‐ else -' + username
        
       def test_username(username) : 
            assert username == "Overridden ‐ else ‐ username" 
Copy the code