fhwang.net

sample_models 1.0

I’ve just released version 1.0 of sample_models. This release has been a long time coming: I’ve been developing sample_models for more than two years as I’ve been trying to figure out the best interface for it. I use it now in a lot of projects, most notably Profitably, so I’m finally able to stabilize the interface and document how this thing works.

sample_models makes it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases. It aims to:

  • meet all your validations automatically
  • only make you specify the attributes you care about
  • give you a rich set of features so you can specify associations as concisely as possible
  • do this with as little configuration as possible

Let’s say you’ve got a set of models that look like this:

  class BlogPost < ActiveRecord::Base
    has_many   :blog_post_tags
    has_many   :tags, :through => :blog_post_tags
    belongs_to :user

    validates_presence_of :title, :user_id
  end

  class BlogPostTag < ActiveRecord::Base
    belongs_to :blog_post
    belongs_to :tag
  end

  class Tag < ActiveRecord::Base
    validates_uniqueness_of :tag

    has_many :blog_post_tags
    has_many :blog_posts, :through => :blog_post_tags
  end 

  class User < ActiveRecord::Base
    has_many :blog_posts

    validates_inclusion_of    :gender, :in => %w(f m)
    validates_uniqueness_of   :email, :login
    # from http://github.com/alexdunae/validates_email_format_of
    validates_email_format_of :email
  end

You can get a valid instance of a BlogPost by calling BlogPost.sample in a test environment:

  blog_post1 = BlogPost.sample
  puts blog_post1.title             # => some non-empty string
  puts blog_post1.user.is_a?(User)  # => true

  user1 = User.sample
  puts user1.email                  # => will be a valid email 
  puts user1.gender                 # => will be either 'f' or 'm'

Since SampleModels figures out validations and associations from your ActiveRecord class definitions, it can usually fill in the required values without any configuration at all.

If you care about specific fields, you can specify them like so:

  blog_post2 = BlogPost.sample(:title => 'What I ate for lunch')
  puts blog_post2.title             # => 'What I ate for lunch'
  puts blog_post2.user.is_a?(User)  # => true

You can specify associated records in the sample call:

  bill = User.sample(:first_name => 'Bill')
  bills_post = BlogPost.sample(:user => bill)

  funny = Tag.sample(:tag => 'funny')
  sad = Tag.sample(:tag => 'sad')
  funny_yet_sad = BlogPost.sample(:tags => [funny, sad])

You can also specify associated records by passing in hashes or arrays:

  bills_post2 = BlogPost.sample(:user => {:first_name => 'Bill'})
  puts bills_post2.user.first_name  # => 'Bill'

  funny_yet_sad2 = BlogPost.sample(
    :tags => [{:tag => 'funny'}, {:tag => 'sad'}]
  )
  puts funny_yet_sad2.tags.size     # => 2

You can also specify associated records by passing them in at the beginning of the argument list, if there’s only one association that would work with the record’s class:

  jane = User.sample(:first_name => 'Jane')
  BlogPost.sample(jane, :title => 'What I ate for lunch')

For more documentation, see the README.

blog comments powered by Disqus
Tagged: ruby, testing, profitably

« Previous post

Next post »