Handbook
Fixtup
presents itself as a toolkit for managing and customizing environments
to run your tests and improve your Quality Assurance process.
Mount a postgresql database for a test
Using the docker plugin (present by default), a fixture can declare service to start when the fixture is mounted.This declaration is a docker-compose.yml
file at the root of the fixture.
When the test starts, the fixture will run the containers from the working directory mounted from the fixture.
.
├── pyproject.toml
├── setup.cfg
└── tests
└── fixtures
└── database_context
├── docker-compose.yml
├── fixtup.yml
└── .hooks
├── hook_mounted.py.sample
├── hook_started.py
├── hook_stopped.py.sample
└── hook_unmounted.py.sample
To emulate postgresql, you declare the container in the docker-compose.yml
file.
version: '3'
services:
mydb:
image: postgres
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=1234
volumes:
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
The loading of the container may take some time. Instead of waiting the container to be ready in your own test, Fixtup
gives you a way to implement a hook called on the fixture has been started.
This hook allow you to wait the availability of the port 5432, the port of the postgresql database.
import fixtup
fixtup.helper.wait_port(5432, timeout=2000)
Note
more about fixtup.plugins.docker
Override environnement variables in a test
Environment variables are the way to specify the configuration to adopt at runtime in a twelve factor architecture.
When the fixtup.plugins.env
plugin is active, you can overridde or add any variable through a .env
file in the
fixture template directory.
# describe the environment variables you want
# load on test start
#
# the original environment is restored
# when the test ends
#
# VAR1="HELLO"
TMP_DIRECTORY=/tmp
Note
more about fixtup.plugins.dotenv
Keep a postgresql running running between all the tests
The keep_up
policy keeps the environment loaded after the fixture is used, until the test runner stops. Once the fixture is started, it will remain up during all tests.
This policy is useful when the docker stack takes too much time to start and stop. We will reuse this stack between all our tests. For example, if your fixture mounts a postgresql database, the database will stay up and running between all your tests.
keep_up: true
Warning
You cannot use 2 postgresql databases on the same port in 2 different fixtures
if you are using a fixture with the keep_up
policy.
Note
more about fixture livecycle
Wait for the availability of a service before running a test
The following example waits for port 5432 to respond on a postgresql dtabase. It uses the hook_started.py
hook. The call to fixtup.helper.wait_port
is blocking. As long as port 5432 does not respond, your test will not start. If a timeout occurs, your test fails.
import fixtup
fixtup.helper.wait_port(5432, timeout=2000)
Note
more about fixture hook
Populate a database with data before running a test
You can use sqlalchemy
in a hook to bootstrap the schema of sqlalchemy and mount
data inside a sqlalchemy
managed database as sqlite
and postgres
.
Le hook hook_setup_data
est exécuté avant de jouer le test.
import kanban.database
from kanban.database import db_session
from kanban.model import BoardColumn, WorkItem
kanban.database.reset_db()
db_session.add(BoardColumn(pid=1, step_name="TODO", wip_limit=None))
db_session.add(BoardColumn(pid=2, step_name="DOING", wip_limit=4))
db_session.add(BoardColumn(pid=3, step_name="DONE", wip_limit=None))
db_session.commit()
db_session.add(WorkItem(pid=1, title='implement feature AAA', column=1, description='xxxxxxxxxxxxxxxxxxxx'))
db_session.add(WorkItem(pid=2, title='implement feature BBB', column=1, description='xxxxxxxxxxxxxxxxxxxx'))
db_session.add(WorkItem(pid=3, title='implement feature CCC', column=3, description='xxxxxxxxxxxxxxxxxxxx'))
db_session.add(WorkItem(pid=12, title='implement feature XXX', column=1, description='xxxxxxxxxxxxxxxxxxxx'))
db_session.commit()
A working example is present in fixtup repository.
Note
In case we need to clean up the database at the end of the test, the hook_teardown_data
hook allows us to do so.
Note
more about fixture hook
Mount a fixture in place
To use 2 complementary fixtures, one that mount a database in a docker container and one taht mount a dataset, only the fixture that mount the dataset has to override the working directory. For the fixture that mount the database, it has to be mounted straight in the template directory.
The flag mount_in_place
in fixtup.yml mount the fixture straight in the template directory.
keep_up: true
mount_in_place: true
import unittest
import os
import fixtup
class UtilsTest(unittest.TestCase)
def test_thumbnail_should_generate_thumbnail(self):
with fixtup.up(['database', 'dataset1']):
# Given
wd = os.getcwd()
# ...
Use Fixtup in a pytest fixture
To write once the initialization code of a fixture of Fixtup
and use it in many tests, you can write a fixture for
pytest
.
def thumbnail_context():
with fixtup.up('thumbnail_context'):
yield None
def test_thumbnail_should_generate_thumbnail(thumbnail_context):
# Given
wd = os.getcwd()
original_file = os.path.join(wd, 'img1.png')
expected_thumbnail_file = os.path.join(wd, 'img1_t.png')
# When
thumbnail(original_file, expected_thumbnail_file)
# Then
self.assertTrue(os.path.isfile(expected_thumbnail_file)
Use Fixtup with other test frameworks
Fixtup
is agnostic to the testing framework. You should be able to use it with
other frameworks like robotframework, …
Keep a fixture after the end of the test runner for debug purpose
At the end of the tests, whatever the fixtup policities, mounted fixtures are cleanup. In some case, you want to keep some of them to debug what has been done inside.
You should use the flag keep_mounted_fixture to keep them in the tmp directory. You will be able to explore what is inside the directories of mounted fixtures and even run the artefact manage by fixtup plugins that are not clean up as well (containers for example, …).
import unittest
import os
import fixtup
class UtilsTest(unittest.TestCase)
def test_thumbnail_should_generate_thumbnail(self):
with fixtup.up('thumbnail_context', keep_mounted_fixture=True):
# Given
wd = os.getcwd()
# ...