如何在所有类中的所有测试之前运行方法?

我正在编写硒测试,其中包含一组类,每个类包含多个测试。每个类目前打开然后关闭 Firefox,这有两个结果:

  • 超级慢,打开 Firefox 比在类中运行测试要花费更长的时间..。
  • 崩溃,因为在 Firefox 被关闭后,试图从硒元素中很快地重新打开它,结果是‘ Error 54’

我可以解决错误54,也许,通过添加一个睡眠,但它仍然是超级慢。

因此,我想做的是在 所有测试类之间重用相同的 Firefox 实例。这意味着我需要在所有测试类之前运行一个方法,在所有测试类之后运行另一个方法。因此,‘ setup _ class’和‘ teardown _ class’是不够的。

82137 次浏览

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.

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:

conftest hooks

Write a pytest_configure or pytest_sessionstart hook in your conftest.py file:

# 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.
"""

pytest plugin

Create a pytest plugin with pytest_configure and pytest_unconfigure hooks.
Enable your plugin in conftest.py:

# 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

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: From documentation:

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 pytest_sessionstart(session) in conftest.py

Example:

# 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')


Log:

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