Using the fixture command

There are several issues you may run into while working with fixtures:

  1. The data model of a program is usually an implementation detail. It’s bad practice to “know” about implementation details in tests because it means you have to update your tests when those details change; you should only have to update your tests when an interface changes.
  2. Data accumulates very fast and there is already a useful tool for slicing and dicing data: the database! Hand-coding DataSet classes is not always the way to go.
  3. When regression testing or when trying to reproduce a bug, you may want to grab a “snapshot” of the existing data.

fixture is a shell command to address these and other issues. It gets installed along with this module. Specifically, the fixture command accepts a path to a single object and queries that object using the command options. The output is python code that you can use in a test to reload the data retrieved by the query.

Note

WARNING: This feature is not fully implemented and doesn’t support all backends.

Usage

$ fixture --help
Usage: fixture [options] OBJECT_PATH

Using the object specified in the path, generate DataSet classes (code) to
reproduce its data.  An OBJECT_PATH can be a python path or a file path
or anything else that a handler can recognize.

When targetting Python objects the OBJECT_PATH is dot separated.
For example, targetting the Employee class in models.py would look like:

    directory_app.models.Employee

Options:
  -h, --help            show this help message and exit
  --dsn=DSN             Sets db connection for a handler that uses a db
  -w WHERE, --where=WHERE
                        SQL where clause, i.e. "id = 1705"
  --suffix=SUFFIX       String suffix for all dataset class names (default:
                        Data; i.e. an Employee object becomes EmployeeData)
  --prefix=PREFIX       String prefix for all dataset class names (default:
                        None)
  --env=ENV             Module path to use as an environment for finding
                        objects.  declaring multiple --env values will be
                        recognized
  --require-egg=REQUIRED_EGGS
                        A requirement string to enable importing from a module
                        that was installed in multi-version mode by
                        setuptools.  I.E. foo==1.0.  You can repeat this
                        option as many times as necessary.
  --template=TEMPLATE   Template to use; choices: ('fixture', 'testtools'),
                        default: 'fixture'
  -c FUNCTION_PATH, --connect=FUNCTION_PATH
                        Path to a function that performs a custom connection,
                        accepting a single parameter, DSN.  I.E.
                        'some_module.submod:connect' will be called as
                        connect(DSN).  Called *after* OBJECT_PATH is imported
                        but *before* any queries are made. This option can be
                        declared multiple times.
  -s FUNCTION_PATH, --setup=FUNCTION_PATH
                        Path to a function that sets up data objects,
                        accepting no parameters. I.E.
                        'some_module.submod:setup_all' will be called as
                        setup_all().  Called *after* OBJECT_PATH is imported
                        but *before* any queries are made and *before*
                        connect(DSN) is called. This option can be declared
                        multiple times.

An Example With SQLAlchemy

Here is a data model defined in SQLAlchemy code. (For complete code see fixture/examples/db/sqlalchemy_examples.py.)

>>> from sqlalchemy import *
>>> from sqlalchemy.orm import *
>>> metadata = MetaData()
>>> authors = Table('authors', metadata,
...     Column('id', Integer, primary_key=True),
...     Column('first_name', String(100)),
...     Column('last_name', String(100)))
...
>>> class Author(object):
...     pass
...
>>> books = Table('books', metadata,
...     Column('id', Integer, primary_key=True),
...     Column('title', String(100)),
...     Column('author_id', Integer, ForeignKey('authors.id')))
...
>>> class Book(object):
...     pass
...
>>> def setup_mappers():
...     mapper(Author, authors)
...     mapper(Book, books, properties={
...         'author': relation(Author)
...     })
...
>>> def connect(dsn):
...     metadata.bind = create_engine(dsn)
...
>>>

After inserting some data, it would be possible to run a command that points at the Book object. The above command uses Book to send a select query with a where clause WHERE title='Dune', and turns the record sets into DataSet classes:

$ fixture fixture.examples.db.sqlalchemy_examples.Book \
      --dsn=sqlite:////tmp/fixture_example.db \
      --where="title='Dune'" \
      --connect=fixture.examples.db.sqlalchemy_examples:connect \
      --setup=fixture.examples.db.sqlalchemy_examples:setup_mappers
import datetime
from fixture import DataSet
from fixture.examples.db.sqlalchemy_examples import authors
from fixture.examples.db.sqlalchemy_examples import books


class authorsData(DataSet):
    class authors_1:
        first_name = u'Frank'
        last_name = u'Herbert'
        id = 1

class booksData(DataSet):
    class books_1:
        author_id = authorsData.authors_1.ref('id')
        id = 1
        title = u'Dune'

Notice that we queried the Book object but got back Table objects. Also notice that all foreign keys were followed to reproduce the complete chain of data (in this case, the authors table data).

Also notice that several hooks were used, one to connect the metadata object by DSN and another to setup the mappers. See Usage above for more information on the --connect and --setup options.

Creating a custom data handler

No documentation yet

Table Of Contents

Previous topic

Using Fixture To Test Django

Next topic

TempIO: A Temporary File System