Quickstart

A simple example

Let’s say we have the following model:



class Toaster(object):

    def __init__(self, color, slots=2, content=None):
        self.color = color
        self.slots = slots
        self.content = content

    def __repr__(self):
        return "<Toaster '%s'>" % self.color


class User(object):

    def __init__(self, toasters):
        self.toasters = toasters

Let’s define a very simple fixtures YAML file:

toaster:   # The fixture's name
  fields:  # The fixture's content
    color: red
    slots: 5
    content: !rel toasts  # You can reference other fixtures
  model: charlatan.tests.fixtures.simple_models:Toaster

toaster_green:
  # Charlatan also supports inheritance
  inherit_from: toaster
  fields:
    color: green

toasts:
  # No model is defined, so it defaults to what `fields` actually is, i.e.
  # in our case, a list.
  fields:
    - "Toast 1"
    - "Toast 2"

In this example:

  • toaster and toasts are the fixture keys.
  • fields is provided as argument when instantiating the class: Toaster(**fields).
  • model is the path to the model that we defined.
  • !rel lets you create relationships by pointing to another fixture key.

You first need to load a fixtures file (do it once for the whole test suite) with charlatan.FixturesManager.load():

>>> import charlatan
>>> fixtures_manager = charlatan.FixturesManager()
>>> fixtures_manager.load("./docs/examples/simple_fixtures.yaml",
...     models_package="toaster.models")
>>> toaster = fixtures_manager.install_fixture("toaster")
>>> toaster.color
'red'
>>> toaster.slots
5
>>> toaster.content
['Toast 1', 'Toast 2']

Voila!

Factory features

Charlatan provides you with factory features. In particular, you can override a fixture’s defined attributes:

>>> toaster = fixtures_manager.install_fixture("toaster",
...     overrides={"color": "blue"})
>>> toaster.color
'blue'

You can also use inheritance:

>>> toaster = fixtures_manager.install_fixture("toaster_green")
>>> toaster.color
'green'

Using charlatan in test cases

Charlatan works best when used with unittest.TestCase. Your test class needs to inherit from charlatan.FixturesManagerMixin.

Charlatan uses an internal cache to store fixtures instance (in particular to create relationships). If you are resetting your database after each tests (using transactions or by manually truncating all tables), you need to clean the cache in TestCase.setUp(), otherwise Charlatan will try accessing objects that are not anymore in the sqlalchemy session.

import unittest

import charlatan

fixtures_manager = charlatan.FixturesManager()
fixtures_manager.load("./docs/examples/simple_fixtures.yaml")


class TestToaster(unittest.TestCase, charlatan.FixturesManagerMixin):

    def setUp(self):
        # Attach the fixtures manager to the instance
        self.fixtures_manager = fixtures_manager
        # Cleanup the cache
        self.init_fixtures()

    def test_example(self):
        """Verify that we can get fixtures."""
        toaster = self.install_fixture("toaster")
        self.assertEqual(toaster.color, "red")
        self.assertEqual(toaster.slots, 5)
        self.assertEqual(toaster.content, ['Toast 1', 'Toast 2'])

Using fixtures

There are multiple ways to require and use fixtures. When you install a fixture using the charlatan.FixturesManagerMixin, it gets attached to the instance and can be accessed as an instance attribute (e.g. self.toaster).

For each tests, in setUp and tearDown

class MyTest(FixturesManagerMixin):

    def setUp(self):
        # This will create self.toaster and self.brioche
        self.install_fixtures(("toaster", "brioche"))

    def test_toaster(self):
        """Verify that a toaster toasts."""
        self.toaster.toast(self.brioche)

For a single test

class MyTest(FixturesMixin):

    def test_toaster(self):
        self.install_fixture("toaster")

With pytest

It’s extremely easy to use charlatan with pytest. There are multiple ways to achieve nice readability, here’s one possibility.

In conftest.py:

import pytest


@pytest.fixture
def get_fixture(request):
    request.addfinalizer(fixtures_manager.clean_cache)
    return fixtures_manager.get_fixture

In your test file:

def test_toaster(get_fixture):
    """Verify that a toaster toasts."""
    toaster = get_fixture('toaster')
    toast = get_fixture('toast')
    ...

Getting a fixture without saving it

If you want to have complete control over the fixture, you can also get it without saving it nor attaching it to the test class:

class MyTest(FixturesManagerMixin):

    def test_toaster(self):
        self.toaster = self.get_fixture("toaster")
        self.toaster.brand = "Flying"
        self.toaster.save()

What happens when you install a fixture

Here’s the default process (you can modify part or all of it using Hooks or Builders):

  1. The fixture is instantiated: Model(**fields).
  2. If there’s any post creation hook, they are run (see Post creation for more information).
  3. The fixture is then saved. If it’s a sqlalchemy model, charlatan will detect it, add it to the session and commit it (db_session.add(instance); db_session.commit()). If it’s not a sqlalchemy model, charlatan will try to call a save method on the instance. If there’s no such method, charlatan will do nothing.

Hooks are also supported.

Uninstalling fixtures

Because charlatan is not coupled with the persistence layer, it does not have strong opinions about resetting the world after a test runs. There’s multiple ways to handle test tear down:

  • Wrap test inside a transaction (if you’re using sqlalchemy, its documentation has a good explanation about how to achieve that).
  • Drop and recreate the database (not really efficient).
  • Install and uninstall fixtures explicitly (you have to keep track of them though, if you forget to uninstall one fixture it will leak in the other tests). See charlatan.FixturesManager.uninstall_fixture().