Template Databases¶
Note
This feature was added in v2.4.0 and currently only supports Postgres.
The template_database fixture keyword argument, and :class:StaticStatements
did not exist prior to this version.
By default, the supported fixtures attempt to amortize the cost of performing fixture
setup through the creation of database templates
(postgres).
If, for whatever reason, this feature does not interact well with your test setup,
you can disable the behavior by setting template_database=False
.
With this feature enabled, all actions considered to be “safe” to statically will performed exactly once per test session, in a template database. This amortizes their initial cost and offloads the majority of the work to postgres itself. Then all “dynamic” actions will be performed on a per-test-database basis.
Consider the following fixture
from models import Base, Example
from pytest_mock_resources import create_postgres_fixture, StaticStatements, Statements, Rows
def some_setup(engine):
# some complex logic, not able to be performed as a `Statements`
pg = create_postgres_fixture(
Base,
Rows(Example(id=1)),
StaticStatements('INSERT INTO foo () values ()'),
Statements('INSERT INTO foo () values ()'),
some_setup,
)
Each of the arguments given to create_postgres_fixture
above are “actions” performed
in the given order. Typically (in particular for non-postgres fixtures, today),
all of the steps would be performed on a completely empty database prior to the
engine/session being handed to the test function.
Static Actions¶
Static actions are actions which are safe to be executed exactly once, because they have predictable semantics which both safely be executed once per test session, as well as happen in a completely separate transactions and database, from the one handed to the test.
Static actions include:
MetaData
:Base
in the above example is an alias toBase.metadata
; i.e. asqlalchemy.MetaData
object that creates all objects defined on the metadata.Rows
: Rows only work in the context of a session, and essentially define a set ofINSERT
statements to be run.StaticStatements
: Are exactly like aStatements
object. The subclass is simply a sentinel to signify that the user asserts their statement is “safe” to be executed as one of these static actions.
Dynamic Actions¶
Dynamic actions have unpredictable semantics, and as such the library cannot safely amortize their cost.
Dynamic actions include:
Statements
: A statement can be any arbitrary SQL, which therefore means we cannot know whether it will react negatively with the test, if executed in a separate transaction. If you’re executing typicalCREATE
/INSERT
statements, preferStaticStatements
.Functions: Obviously a function can do anything it wants, and therefore must be dynamic.
warning
It is important to consider action ordering when using dynamic actions. Upon encountering a dynamic action in a list of ordered actions, all subsequent actions will be executed as though they were dynamic (i.e. per-test-database).
You should therefore prefer to group all static actions before dynamic ones wherever possible to ensure you get the most optimal amortization of actions.
For example:
pg = create_postgres_fixture(
Statements('CREATE TABLE ...'),
Base,
)
This will execute all setup dynamically because it encountered a dynamic action
(Statements
in this case) first. Ideally the above actions would be reversed,
or that the Statements
be swapped for a StaticStatements
.