Testing Paperclip with Shoulda

Josh Susser had a bunch of great things to say about Shoulda in his RailsConf 2008 presentation, The great test framework dance-off. One of the parts he really liked was the conceptual simplicity of creating macros when drying up your tests.

It’s a misnomer to even call what you do with Shoulda “macros”, since they’re just normal class methods. Here’s one we use all the time for our projects that use Paperclip (which you should definitely check out if you haven’t all ready).

# in test_helper.rb...
class Test::Unit::TestCase
  def self.should_have_attached_file(attachment)
    klass = self.name.gsub(/Test$/, '').constantize

    context "To support a paperclip attachment named #{attachment}, #{klass}" do
      should_have_db_column("#{attachment}_file_name",    :type => :string)
      should_have_db_column("#{attachment}_content_type", :type => :string)
      should_have_db_column("#{attachment}_file_size",    :type => :integer)

    should "have a paperclip attachment named ##{attachment}" do
      assert klass.new.respond_to?(attachment.to_sym),
              "@#{klass.name.underscore} doesn't have a paperclip field named #{attachment}"
      assert_equal Paperclip::Attachment, klass.new.send(attachment.to_sym).class

# And in the Tests...
class UserTest < Test::Unit::TestCase
  should_have_attached_file :avatar

One of Shoulda’s main goals is to remain as simple to understand and extend as possible. While there’s a little bit of magic when getting the current class from the test class name, the rest of the macro is concise and easy to understand.

We use this technique liberally throughout our test suites on a variety of applications, and it’s worked wonders. We even push the general purpose ones into Shoulda proper. If you have any that you think should be shared with the community, just send us a git pull request.

Pair with one of our expert developers to level up your skills with Coaching by thoughtbot. Save time learning best practices and techniques for reducing technical debt in Ember, Ruby, Haskell, and Go in 1-on-1 sessions tailored to your goals.