Fixtures without validation with Factory Girl

Factory Girl is my fixture replacement library of choice. It improves tests readability and maintainability. It’s also customizable.

There are sometimes situations when you want to create test scenarios that checks how your app is handling invalid data (not user input, but invalid records that already sit in your database). To do this you first need to put this invalid data to your db.

You could accomplish this with such line:

@user = Factory(:user, :email => "not a correct email address")

However, factory girl would raise an exception here, because that’s the default strategy of creating new fixtures – raise exception if save fails (because of validation errors for example).

Thankfully we can use our own strategy of creating new fixtures, such that does save records without validation.

First let’s define our new strategy:

class Factory::Proxy::CreateWithoutValidation < Factory::Proxy::Build
  def result
    @instance.save(false)
    @instance
  end
end
 
class Factory
  def self.create_without_validation (name, overrides = {})
    factory_by_name(name).run(Proxy::CreateWithoutValidation, overrides)
  end
end

Now we can use it while defining new factory:

Factory.define :invalid_user, :class => User, :default_strategy => :create_without_validation do |f|
  ...
end

And then we can happily create invalid fixtures without any exceptions raised.

Turning off auto timestamping for testing in Rails

Suppose that you implemented a functionality that depends on values of created_at or updated_at fields of your models. How do you test it?

If you use fixtures that reside in test/fixture/*.yml files then there is no problem, because the values you set there for created_at and updated_at fields are saved to the database ‘as is’. So you can easily have an article created one week ago:

article:
  title: What a great day
  created_at:
  updated_at:

However, I don’t use fixtures files myself. I feel a bit dirty using them ;) I find fixture replacement tools far more maintainable. Namely, I love thoughtbot’s Factory Girl. But here comes the problem. This won’t work as expected with Factory Girl:

Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)

That’s because ActiveRecord’s automatic timestamping feature sets Time.now for created_at and updated_at fields overriding our values. At least that’s ActiveRecord’s default behavior. Fortunately it can be disabled with:

Article.record_timestamps = false

Chances are that after creating a model with a custom timestamp we’ll want to turn automatic timestamping back on. But turning it off and on in many places in your unit tests would be pretty cumbersome. Wouldn’t it be cool if you could achieve all of this with a snippet below?

without_timestamping_of Article do
  Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)
end

It turns timestamping off, executes the block and turns timestamping back on. I find it clean and dry. Here’s the code to place in your test_helper.rb:

# test_helper.rb
class Test::Unit::TestCase # or class ActiveSupport::TestCase in Rails 2.3.x
  def without_timestamping_of(*klasses)
    if block_given?
      klasses.delete_if { |klass| !klass.record_timestamps }
      klasses.each { |klass| klass.record_timestamps = false }
      begin
        yield
      ensure
        klasses.each { |klass| klass.record_timestamps = true }
      end
    end
  end
end

Of course you can turn off timestamping for many models at once:

without_timestamping_of Article, Comment, User do
  Factory(:article, :created_at => 1.week.ago, :updated_at => 1.week.ago)
  Factory(:comment, :created_at => 1.day.ago)
  Factory(:user, :updated_at => 5.hours.ago)
end

Hope you like it. If so, share :)