Snippet for Fun

Flask database test fixture generation with factory_boy

Posted at — Apr 10, 2021

Background

It’s tedious to generate test fixtures by hand, to allow faster iteration, we can employ factory_boy to generate these fixtures automatically for our Python project. What even better is, factory_boy has builtin support for well known ORM libraries, such as SQLALchemy and Django.

Example

Suppose we are building a service that records famous quotes. In this service, we define a Quote model for quote entries like this:

# in app.py

db = SQLAlchemy()


class Quote(db.Model):

    id = db.Column(db.Integer, primary_key=True)
    author = db.Column(db.String(100), nullable=False)
    message = db.Column(db.Text)

Then in the test side (for common testing setup, see Flask database test setup with py.test), we can define a factory for this model:

# in test/factory.py

import factory

from app import db, Quote


class QuoteFactory(factory.alchemy.SQLAlchemyModelFactory):

    class Meta:
        model = Quote
        sqlalchemy_session = db.session

    author = factory.Faker('name')
    message = factory.Faker('text')

Now, we can consume this factory in various places:

Using in REPL

(venv) $ flask shell
App: app [production]
...
>>> from app import Quote
>>> from test.factory import QuoteFactory
>>> quote = QuoteFactory.build()
>>> assert isinstance(quote, Quote)
>>> print(quote.author)
Thomas Miller
>>> print(quote.message)
Doctor game enough with.
Responsibility yourself themselves information. Single support east.
Operation night line parent take act new. She race activity. Air in fish prevent behind.

Using in tests

During the test, we can use XXXFactory.create to create an instance and add it to current test db session:

def test_create_one_quote(db):
    quote = QuoteFactory.create()
    db.session.commit()
    assert quote.id is not None
    assert quote.author
    assert quote.message

We can also override the value for one or multiple fields, by specifying the field name in create call:

def test_create_many_quotes(db):
    expected_author = 'Louis Armstrong'
    quotes = QuoteFactory.create_batch(100, author=expected_author)
    db.session.commit()
    for quote in quotes:
        assert quote.id is not None
        assert quote.author == expected_author
        assert quote.message

Conclusion

With the help of factory_boy, we can generate a large collection of test fixtures without much effort.

A full example of code can be check out from the GitHub repo.