Handbook ######## ``Fixtup`` presents itself as a toolkit for managing and customizing environments to run your tests and improve your Quality Assurance process. .. contents:: :backlinks: top :local: 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. .. code-block:: text . ├── 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. .. code-block:: yaml :caption: tests/fixtures/database_context/docker-compose.yml 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. .. code-block:: python :caption: tests/fixtures/database_context/.hooks/hook_started.py import fixtup fixtup.helper.wait_port(5432, timeout=2000) .. note:: more about :ref:`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. .. code-block:: bash :caption: tests/fixtures/database_context/.env # 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 :ref:`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. .. code-block:: yaml :caption: ./tests/fixtures/postgres_datastore/fixtup.yml 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 :term:`fixture livecycle` .. _HookWaitAvailability: 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. .. code-block:: python :caption: tests/fixtures/simple_postgresql/.hooks/hook_started.py import fixtup fixtup.helper.wait_port(5432, timeout=2000) .. note:: more about :term:`fixture hook` .. _HookLoadData: 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. .. code-block:: python :caption: tests/fixtures/simple_board/.hooks/hook_setup_data.py 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 :term:`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. .. code-block:: yaml :caption: tests/fixtures/database/fixtup.yml keep_up: true mount_in_place: true .. code-block:: python :caption: ./tests/integrations/test_utils.py 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``. .. code-block:: python :caption: ./tests/integrations/test_utils.py 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, ...). .. code-block:: python :caption: ./tests/integrations/test_utils.py 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() # ...