My dev partner and I have spent the last few days creating a stub application that we plan on using as the starting point for all of our future Ruby on Rails applications. It's got all of our standard code and favorite plugins baked right in, and should help reduce the startup time for any new app that we need to create. One of our big goals with this stub app was 100% test coverage with RSpec.

We have a collection of useful chunks of code that represent our own tricks, recipes from various O'Reilly cookbooks, and bits from the Interweb that we find useful. One such tool is an extension to ActiveRecord's validates_associated method; essentially, if you have class Book that has_many Chapters, and Book validates_associated :chapters, the error message that you would normally get if validation fails on an instance of Chapter is 'Chapters is invalid'. The code snippet in our utility class lets errors from associated models "bubble up" into the validating model's errors, so that you can get an error like 'Chapter Two has an invalid page count'.

The problem that we faced in writing our specs is that our stub application doesn't have any models with this sort of relationship. I didn't want to create unneeded models in our stub app just to satisfy our desire for testing coverage, and I wasn't really satisfied with either of the Rails plugins that I found for creating ActiveRecord models without a backing table.

I finally came up with a solution. I'm not sure if it's the most elegant way of solving the problem, but it works, only requires one extra file, and leaves both the development and production environments clean.

1. Create a test-only migration:

000_create_associated_models_for_testing.rb:

class CreateAssociatedModelsForTesting < ActiveRecord::Migration
  def self.up
    if RAILS_ENV == 'test'
      create_table :books do |t|
        t.string  :name
      end
      create_table :chapters do |t|
        t.string  :name
        t.integer :primary_id
      end
    end
  end
  def self.down
    if RAILS_ENV == 'test'
      drop_table :books
      drop_table :tests
    end
  end
end

Checking the RAILS_ENV in the migration ensures that these tables won't actually get created anywhere but the test environment.

2. Perform the migration as part of your test execution:

foo_spec.rb

require File.dirname(__FILE__) + '/../spec_helper'
`rake db:migrate`
...

The migration is run prior to any tests being run in this particular spec.

3. Define inline, throwaway ActiveRecord models:

Inside your describe block in foo_spec.rb:

it 'validates associated models with a meaningful message' do
class Book < ActiveRecord::Base has_many :chapters validates_associated :chapters end
class Chapter < ActiveRecord::Base belongs_to :book validate :name_should_not_be_illegal def name_should_not_be_illegal errors.add_to_base("Chapter name cannot be illegal.") if self.name == 'illegal' end end
my_book = Book.new(:name => "My Folder") my_book.save.should be_true
my_book.chapters.create(:name => "legal").should be_true my_book.save.should be_true
my_book.chapters.create(:name => "illegal").should be_true my_book.save.should be_false
my_book.errors.inspect.include?("Chapter name cannot be illegal.").should be_true end

Here we have defined two arbitrary models, Book and Chapter, inside of the spec.

4. Prep your test database

The only downside to this approach that I have seen is that you have to manually prep your test database, so no rake db:test:clone for you, since the development database does not contain our test tables. Rather, run rake db:migrate RAILS_ENV=test to ensure that the test-only tables are present.

5. Enjoy RSpec coverage of non-database-backed models

This allows us to test our extension to ActiveRecord's validation method without adding any plugins, gems, or other sorts of hacks.

Of course, this would not be necessary if you have actual models whose behaviour you are testing; but if you are the author of a plugin or gem, this approach might provide you with a tool that you can use in writing your specs.

Related Articles


Comments

Leave a Comment


IdolHands.com Spam-o-MeterTM
Bot
Spammer
Moron
Human






* Required fields.