Each Answer to this Q is separated by one/two green lines.
I’m writing selenium tests, with a set of classes, each class containing several tests. Each class currently opens and then closes Firefox, which has two consequences:
- super slow, opening firefox takes longer than running the test in a class…
- crashes, because after firefox has been closed, trying to reopen it really quickly, from selenium, results in an ‘Error 54’
I could solve the error 54, probably, by adding a sleep, but it would still be super slow.
So, what I’d like to do is reuse the same Firefox instances across all test classes. Which means I need to run a method before all test classes, and another method after all test classes. So, ‘setup_class’ and ‘teardown_class’ are not sufficient.
Using session fixture as suggested by hpk42 is great solution for many cases,
but fixture will run only after all tests are collected.
Here are two more solutions:
# content of conftest.py def pytest_configure(config): """ Allows plugins and conftest files to perform initial configuration. This hook is called for every plugin and initial conftest file after command line options have been parsed. """ def pytest_sessionstart(session): """ Called after the Session object has been created and before performing collection and entering the run test loop. """ def pytest_sessionfinish(session, exitstatus): """ Called after whole test run finished, right before returning the exit status to the system. """ def pytest_unconfigure(config): """ called before test process is exited. """
Create a pytest plugin with
Enable your plugin in
# content of conftest.py pytest_plugins = [ 'plugins.example_plugin', ] # content of plugins/example_plugin.py def pytest_configure(config): pass def pytest_unconfigure(config): pass
You might want to use a session-scoped “autouse” fixture:
# content of conftest.py or a tests file (e.g. in your tests or root directory) @pytest.fixture(scope="session", autouse=True) def do_something(request): # prepare something ahead of all tests request.addfinalizer(finalizer_function)
This will run ahead of all tests. The finalizer will be called after the last test finished.
Starting from version 2.10 there is a cleaner way to tear down the fixture as well as defining its scope. So you may use this syntax:
@pytest.fixture(scope="module", autouse=True) def my_fixture(): print ('INITIALIZATION') yield param print ('TEAR DOWN')
The autouse parameter:
Here is how autouse fixtures work in other scopes:
autouse fixtures obey the scope= keyword-argument: if an autouse fixture has scope=”session” it will only be run once, no matter where
it is defined. scope=”class” means it will be run once per class, etc.
if an autouse fixture is defined in a test module, all its test functions automatically use it.
if an autouse fixture is defined in a conftest.py file then all tests in all test modules below its directory will invoke the fixture.
The “request” parameter:
Note that the “request” parameter is not necessary for your purpose although you might want to use it for other purposes. From documentation:
“Fixture function can accept the request object to introspect the
“requesting” test function, class or module context..”
Try to use
# project/tests/conftest.py def pytest_sessionstart(session): print('BEFORE')
# project/tests/tests_example/test_sessionstart.py import pytest @pytest.fixture(scope="module", autouse=True) def fixture(): print('FIXTURE') def test_sessonstart(): print('TEST')
BEFORE ============================================================================ test session starts ============================================================================= platform darwin -- Python 3.7.0, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /Library/Frameworks/Python.framework/Versions/3.7/bin/python3 cachedir: .pytest_cache rootdir: /Users/user/Documents/test, inifile: pytest.ini plugins: allure-pytest-2.8.12, env-0.6.2 collected 1 item tests/6.1/test_sessionstart.py::test_sessonstart FIXTURE TEST PASSED