Easier Database Fixtures

I recently posted about DBIx::Class::EasyFixture and I was shocked to receive an email from someone explaining how he uses it in production with Test::Class::Moose. Basically, the ability to write this was the killer feature for him:

sub test_teardown {
    my $test = shift;
    $test->fixtures->unload;
}

As happy as I am with how easy it is to create and manage database fixtures with DBix::Class::EasyFixture, when I was writing the slides for my Perl testing class, I found that I wanted the fixtures to be even easier (I should call this TDD: Training Driven Development). Previously, if you wanted to create a fixture for a Person and that was automatically linked to an associated Customer class, you would do something like this:

person_bob => {
    new => 'Person',
    using => {
        name     => 'Bob',
        birthday => DateTime->new( ... ),
    },
    next => [qw/customer_bob/],
},
customer_bob => {
    new => 'Customer',
    using => {
        first_purchase => DateTime->new( ... ),
    },
    requires => {
        person_bob => {
            our   => 'person_id',
            their => 'person_id',
    },
},

That was still too verbose for me, so now I support this syntax:

person_bob => {
    new => 'Person',
    using => {
        name     => 'Bob',
        birthday => DateTime->new( ... ),
    },
    next => [qw/customer_bob/],
},
customer_bob => {
    new => 'Customer',
    using => {
        first_purchase => DateTime->new( ... ),
        person_id      => { person_bob => 'person_id' },
    },
 },

However, since both Customer and Person have the same field name for person_id in our example, you can just take a reference to person_bob:

customer_bob => {
    new => 'Customer',
    using => {
        first_purchase => DateTime->new( ... ),
        person_id      => \'person_bob',
    },
 },

So now it's much easier to write fixtures.

It's now also easier to use fixtures because the load() method now returns the fixtures it's loaded:

my $schema   = My::Schema->connect( ... );
my $fixtures = My::Fixtures->new({ schema => $schema });

my $customer  = $schema->load('customer_bob');
my @customers = $schema->load('all_customers');

Called in scalar context, load() always returns the first fixture loaded. In list context, it returns all fixtures loaded in the order they were loaded. Note that this means "requested" fixtures. When you call $schema->load('customer_bob'), it knows to load the person_bob fixture, but that is done internally and not returned via the load() statement. Thus, if person_bob has a next entry causing it to load customer_bob, the following two lines load the same information, but the first will return a Person object and the second will return a Customer object:

my $person    = $fixture->load('person_bob');
# versus
my $customer  = $fixture->load('customer_bob');

And for the second, you can still get the Person object:

my $person = $fixture->get_result('person_bob');

Of course, you can still do things the hard way:

$fixture->load('person_bob');
my $person = $schema->resultset('Person')->find({email => $email});

Enjoy!

As always, if you're interested in hiring me or any of the talented developers we have working at All Around The World, drop me a line at ovid@allaroundtheworld.fr.

Leave a comment

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/