Fixtures¶
lemoncheesecake provides a fixture system similar to what pytest offers (http://doc.pytest.org/en/latest/fixture.html). Fixtures are a powerful and modular way to inject dependencies into your tests.
# fixtures/myfixtures.py:
import requests
import lemoncheesecake.api as lcc
@lcc.fixture(scope="session")
def user_auth(cli_args):
# we assume that custom cli arguments "user" and "password" have been
# previously set through project file
return cli_args.user, cli_args.password
@lcc.fixture(scope="session")
def api(user_auth):
session = requests.Session()
session.auth = user_auth
return session
# suites/my_suite.py:
import lemoncheesecake.api as lcc
SUITE = {
"description": "My Suite"
}
@lcc.test("Some test")
def some_test(api):
resp = api.get("GET", "/some/resource")
[...]
Fixtures can also be injected into suites through parameters passed to setup_suite
and class
instance / module attributes:
# suites/my_suite.py:
import lemoncheesecake.api as lcc
SUITE = {
"description": "My Suite"
}
def setup_suite(api):
[...]
# tests/my_suite.py:
import lemoncheesecake.api as lcc
SUITE = {
"description": "My Suite"
}
api = lcc.inject_fixture()
Fixture name(s)¶
A fixture can be called through multiple names specified in the names
parameter (otherwise the fixture name
is the fixture function name):
@lcc.fixture(names=("fixt_a", "fixt_b")) def fixt(): [...]
Fixture scopes¶
Four fixture scopes are available (higher to lower scope):
pre_run
: fixtures with this scope will be called before the test session is started, meaning that the fixture cannot use any of thelog_*
,check_*
, etc… functions. If a fixture with this scope raises an exception, it will prevent the tests to be executed. This behavior can be used in conjunction with theUserError
exception and thecli_args
fixture to handle bad CLI argumentssession
: fixtures with this scope are initialized at the global levelsuite
: fixtures with this scope are initialized at the test suite level; if a suite “A” uses asuite
scoped fixture (through a test for example), and a sub suite “B” uses the same fixture, then the fixture is initialized two times: one time for “A” and the other time for “B”test
: fixtures with this scope are initialized at the test level
A fixture can use other fixtures as arguments, in this case the scope level compatibility must be respected:
for instance, a test
scoped fixture can use a session
scoped fixture, but the opposite is not true.
Fixtures with scope pre_run
that have been previously executed through a dependency can get be retrieved
using lcc.get_fixture(name)
.
Fixture teardown¶
Fixture teardown can be implemented using yield to initially return the fixture value and then to de-initialize the fixture:
@lcc.fixture() def resource_file(): fh = open("/some/file", "r") yield fh fh.close()
Per-thread fixtures¶
New in version 1.9.0.
When running tests using multiple threads you may need to share objects between tests (that’s what the
session
or suite
-scoped fixtures are about) while those objects are not thread-safe or not intended to be
used simultaneously (think for instance
to a web browser driven by a selenium web driver), in that case you may use
a per-thread fixture:
@lcc.fixture(scope="session", per_thread=True)
def resource():
[...]
With a session-scoped “classical” fixture, the fixture would have been evaluated only once in the test session setup phase and the exact same resulting object would have been passed to the tests where the fixture is used whether they are run sequentially or in parallel.
A per-thread fixture on the other hand is not evaluated at the beginning of the test session, but instead is lazily evaluated when a test uses this fixture on a given test thread. When a new test running on the same thread uses the same fixture, then the fixture value formerly obtained for this thread will be used again. On the contrary, if another test running on another thread is also depending on this fixture, the fixture will be evaluated another time for that thread.
Despite the fixture has the session
scope, it is evaluated at the test
scope. It implies a few
limitations compared to a non-per-thread fixture. A per-thread fixture:
can only be set to the scope
session
orsuite
can only be used in a test or in a test-scoped fixture
Please note that a more low-level approach to per-thread object sharing exists with the ThreadedFactory class if per-thread fixtures do not fit your needs.
Builtin fixtures¶
lemoncheesecake provides several special builtin fixtures:
cli_args
(scope:pre_run
) is the object returned byparse_args
of the argparse module and that contains the actual CLI arguments; this fixture can be used to access custom command line arguments previously setup by the methodadd_custom_cli_args
of the project class declared in the lemoncheesecake project fileproject_dir
(scope:pre_run
) is the path of the project, meaning the directory of the project filefixture_name
is the name of the called fixture and can only be used by a fixture. A typical use case is a fixture with multiple names,fixture_name
can be used to identify through which name the fixture has been called and adapts its behavior accordingly
Using the default project.py
file, fixtures will be loaded from the fixtures
sub directory.