Sep 13 2007

Rails Inline Fixtures

Rails Fixures provide a way of organizing sample data to be used in tests. Traditionally, they reside under the test/fixtures directory of a Rails application.

One of the nice things about fixtures is the declarative nature of their authoring and the simple syntax of their platform (YAML). They are clear, easy to read, short and concise.

One implication that results from using fixtures in accordance to the above strategy is that the data under test is not directly visible in the test itself, i.e. one needs to navigate to a different file to verify parts of the test's setup.

At the same time, the more these fixtures start being shared by more and more tests, the more difficult it becomes to keep them organized in a way that makes one fixture's data directly appropriate for each of the tests that are using it. For example, a fixture, songs.yml, is being used by a unit test that requires two song entries, whereas the acceptance test for the songs page's pagination logic requires 25 songs to be created. The fixtures :songs directive in the song unit test will happily create 25 fixtures each time, 23 of which are never used.

It might be useful to be able to declare fixtures inline on a per test or test case basis, while maintaining the effective, elegant syntax. The code bellow is an attempt to this direction.

[ruby] module InlineFixtures def delete_fixtures(table_name) ActiveRecord::Base.connection.delete "DELETE FROM #{table_name}", 'Fixture Delete' end def load_inline_fixtures(table_name_sym, inline_fixture) table_name = table_name_sym.to_s delete_fixtures table_name yaml_fixture = YAML.load inline_fixture yaml_fixture.values.map do |f| fixture = Fixture.new f, table_name ActiveRecord::Base.connection.execute "INSERT INTO `#{table_name}` (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert' end end alias :___ :load_inline_fixtures end [/ruby]

Including the InlineFixtures module allows for code that can be used anywhere inside a ruby script and looks like this:

[ruby] class SongTest This technique has proven to be particularly useful in our Selenium On Rails tests (written in rselenese), as these tests commonly require the most elaborate database setup.