On 2/3/07, Allen Bierbaum <[EMAIL PROTECTED]> wrote:
>
> This works for creating the table structure, and that is important,
> but for me I think the actual data in the database is just was
> important if not more so. What are people using to populate the
> database with test data to use in the tests?

if you feel adventurous, I've been working to implement SQLAlchemy in
the fixture module :

http://code.google.com/p/fixture/

It's not too difficult to run insert statements at the top of your
tests, or inject an SQL file.  But when you want to make assertions
using that data things get tricky, which is why I created the fixture
module.  To give you a very rough example of how this works (since the
docs are very lacking at the moment), you have 1) DataSet objects to
define data and 2) a Fixture object that knows how to load that data.

In fact, special for you--!--and because I've been meaning to do so, I
just checked in this example, to illustrate the point (if formatting
goes funny, it is here:
http://fixture.googlecode.com/svn/trunk/fixture/examples/db/sqlalchemy_examples.py
).  Before I get to it, I want to quickly address the point you made
about having a gigabyte database but only needing a sample of it for
testing.  I actually find the simplest way to work with fixtures is to
use existing data.  So the module also includes a command line that
can generate fixture code from a data source, given an object that
knows how to select.  I.E.

$ fixture path.to.a.table.widgets --dsn=postgres://[EMAIL PROTECTED]/db
--query="widget_id=2"

will build fixture code (including all foreign key refs) by creating a
select statement using the widgets table.  This command isn't
implemented fully and may be rough around the edges but it works for
something like above.  Now ... that example:


SequencedSet Fixtures with SQLAlchemy and nose
----------------------------------------------

Create your tables::

    >>> from sqlalchemy import *
    >>> from sqlalchemy.ext.sessioncontext import SessionContext
    >>> from sqlalchemy.ext.assignmapper import assign_mapper

    >>> meta = BoundMetaData("sqlite:///:memory:")
    >>> session_context = SessionContext(
    ...     lambda: create_session(bind_to=meta.engine))
    ...
    >>> affiliates = Table('affiliates', meta,
    ...     Column('id', INT, primary_key=True),
    ...     Column('name', String),)
    ...
    >>> class Affiliate(object): pass
    >>> m = assign_mapper(session_context, Affiliate, affiliates)
    >>> events = Table('events', meta,
    ...     Column('id', INT, primary_key=True),
    ...     Column('type', String),
    ...     Column('affiliate_id', INT,
    ...         ForeignKey('affiliates.id')),)
    ...
    >>> class Event(object): pass
    >>> m = assign_mapper(session_context, Event, events, properties = {
    ...                                 'affiliate': relation(Affiliate), })

Note that using mappers above is not necessary.  The fixture module also
supports interacting with mapper classes, however.

Next you build the DataSet objects that you want to load, in this case they
inherit from SequencedSet, an optional DataSet enhancement that simulates
auto-incrementing IDs.  The IDs values can be overridden for any row and the
column name is configurable, but defaults to 'id'::

    >>> from fixture import SequencedSet
    >>> class affiliates_data(SequencedSet):
    ...     class joe:
    ...         name="Joe, The Affiliate"
    ...
    >>> class events_data(SequencedSet):
    ...     class joes_click:
    ...         affiliate_id = affiliates_data.joe.ref('id')
    ...         type="click"
    ...     class joes_submit(joes_click):
    ...         type="submit"
    ...     class joes_activation(joes_click):
    ...         type="activation"
    ...

Note how joes_submit inherits from joes_click.  Inheritance is a natural way to
share common column values (in this case, all events belong to the same
affiliate).

Next you need a module-level Fixture instance that knows how to load the above
DataSet object(s).  We are also going to tell it to derive SQLAlchemy table
names by looking in the global scope and chopping off "_data" from the DataSet
class name (there are other ways to do this more or less explicitly).

We are going to pass it a session_context to create connections with, but again
there are alternatives to this::

    >>> from fixture.style import TrimmedNameStyle
    >>> from fixture import SQLAlchemyFixture
    >>> db = SQLAlchemyFixture( env=globals(), session_context=session_context,
    ...                         style=TrimmedNameStyle(suffix="_data"))
    ...

Now we are ready to write a test that uses the fixtures.  The
following is just one way you could write a test function runnable by
nose_ ::

    >>> def setup_data():
    ...     meta.create_all()
    ...
    >>> def teardown_data():
    ...     meta.drop_all()
    ...     clear_mappers()
    ...     # and do whatever else ...
    ...
    >>> @db.with_data(events_data, setup=setup_data, teardown=teardown_data)
    ... def test_event_something(data):
    ...     joe = Affiliate.get(data.affiliates_data.joe.id)
    ...     click = Event.get(data.events_data.joes_click.id)
    ...     assert click.affiliate is joe
    ...     assert click.type == data.events_data.joes_click.type
    ...

The rest will be done for you automatically by nose_::

    >>> import nose, unittest
    >>> result = unittest.TestResult()
    >>> case = nose.case.FunctionTestCase(test_event_something)
    >>> case(result)
    >>> result.testsRun
    1
    >>> result.errors
    []
    >>> result.failures
    []

Here are some things to note.  @db.with_data() takes an arbitrary number of
DataSet classes as an argument and passes an instance of Fixture.Data to the
test function.  Because of the reference to affiliates_data, you don't have to
specify that set since it will be discovered by reference.  Also note
that there
are other ways to load data.  Say, if you want to work with unittest.TestCase
classes you could create the data instance you see above manually in a setUp()
def, like data = db.data(events_data)

.. _nose: http://somethingaboutorange.com/mrl/projects/nose/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to