Unit testing and the schema

Hi! I’m a newbie wrapping my head around the Rails way of designing apps
at the database layer, and I’ve hit a minor conceptual logjam and was
hoping some of you might be able to help work me through it.

I’m (re)designing a PHP audio jukebox in RoR (I’m going to be migrating
a big pile of data from a system called Netjuke, which has been folded
into the Jinzora project). I have some very definite ideas about how the
data ought to be structured and how relationships ought to work, and the
metadata schema I’ve designed generates lots of complicated dependencies
and relationships which I feel are best specified and enforced at the
database level. For now I’m working in PostgreSQL, but I might move this
all over to Oracle at some point.

So my toy version of the app has three classes of entities: artists,
albums and tracks. Albums and tracks belong to artists (think
compilations or soundtracks for why that notion of ownership needs to be
at both levels) and tracks also belong to albums. I’m building the
schema via migrations, but have slapped in migrations that look like:

class AddConstraints < ActiveRecord::Migration
def self.up
execute ‘ALTER TABLE tracks ADD FOREIGN KEY(album_id) REFERENCES
albums ON DELETE RESTRICT’
execute ‘ALTER TABLE tracks ADD FOREIGN KEY(artist_id) REFERENCES
artists ON DELETE RESTRICT’
execute ‘ALTER TABLE albums ADD FOREIGN KEY(artist_id) REFERENCES
artists ON DELETE RESTRICT’
end

def self.down
execute ‘ALTER TABLE tracks DROP CONSTRAINT tracks_artist_id_fkey’
execute ‘ALTER TABLE tracks DROP CONSTRAINT tracks_album_id_fkey’
execute ‘ALTER TABLE albums DROP CONSTRAINT albums_artist_id_fkey’
end
end

My problem is that using fixtures and transactional unit tests, I have
to incorporate all the fixtures for the classes in the foreign key
restrictions to ensure that fixtures get set up and torn down correctly.
I.e. unless I have a line that says “fixtures :artists, :albums,
:tracks” in test/unit/(album|artist|track)_test.rb, the test database
isn’t left in a sane state across unit tests and unit tests start
failing due to integrity constraint errors when the fixture code tries
to empty out the table during the fixture setup invoked by the fixtures
method.

It was a bit of an effort to figure all that out, and my solution feels
hacky. Why should I have to set up fixtures for tracks and albums when
I’m only testing artists, who sit at the top of this hierarchy? And
how’s that going to play when I add in many-to-many relationships
between groups (artists) and their members? And glom genres onto this?
It seems like this is going to make my test cases increasingly complex
and unwieldy.

Is it already time for me to abandon using “fixtures” in my test cases
and start doing explicit fixture setup and teardown? Or is there a
simple way I can modify the framework, e.g. to automatically clear out
all of the fixtures used by an individual test case? I can’t imagine I’m
the only person who’s run into this.

Thanks in advance to any insight you might be able to provide. RoR is
totally making it fun for me to work on this project again, and for that
alone I’m very thankful.

Forrest

Yeah, that’s basically the deal, though it’s not obvious from the
RDocs. You can pre-populate the database before your tests run and
use transactional_fixtures. Transactional fixtures are perhaps
misnamed, as any database changes made by your tests roll back, but
the fixtures do NOT. Fixtures are still deleted, inserted and
committed for each test.

I admit that I didn’t like the approach either – in previous Java
projects, the team introduced nasty sequence problems in tests when
we relied on pre-populated data. But after I fiddled with the order
of my fixtures to account for foreign key constraints, it works fine
and the transactions keep the test fast.

Scott

On Dec 4, 2005, at 2:47 PM, Forrest L Norvell wrote:

Hi! I’m a newbie wrapping my head around the Rails way of designing
apps
at the database layer, and I’ve hit a minor conceptual logjam and was
hoping some of you might be able to help work me through it.

— Scott W. [email protected] wrote:

committed for each test.

I admit that I didn’t like the approach either – in
previous Java
projects, the team introduced nasty sequence
problems in tests when
we relied on pre-populated data. But after I fiddled
with the order
of my fixtures to account for foreign key
constraints, it works fine
and the transactions keep the test fast.

The approach also causes problems if you have triggers
that disallow deletes from tables. We have data that
we are required by law to only delete after 7,
sometimes 20 years. So I created a trigger to prevent
that from occuring. We ended up rewriting how the
tests work from rake to building the schema from
scratch every time. It fortunately only takes about 20
seconds.

Shawn G.


Yahoo! DSL ? Something to write home about.
Just $16.99/mo. or less.
dsl.yahoo.com