Introducing the Shoulda Testing Plugin
Posted by Tammer Saleh
Apr 06
So we’ve been hinting for some time now that we were going to tidy up the testing helpers we use here, and that’s just what we’ve done.
Update: Added the Why? section below to address rSpec & Test::Spec
Introducing Shoulda!
We had a very hard time coming up with a name for this plugin, mostly because of its somewhat eclectic nature. It does three main things:
Contexts
Shoulda defines context and should blocks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class UserTest < Test::Unit::TestCase def setup # Normal setup code end def test_can_have_normal_tests # Normal test here end context "a User instance" do setup do @user = User.find(:first) end should "return its full name" assert_equal 'John Doe', @user.full_name end end end |
This is very much like the rSpec way of doing things, but they can be used along-side normal test method definitions. Also, these context blocks can be nested.
Macros
Shoulda also attempts to collect common test patterns into test macros:
1 2 3 4 5 6 7 8 9 10 11 12 |
class UserTest < Test::Unit::TestCase should_require_attributes :name, :phone_number should_not_allow_values_for :phone_number, "abcd", "1234" should_allow_values_for :phone_number, "(123) 456-7890" should_protect_attributes :password should_have_one :profile should_have_many :dogs should_have_many :messes, :through => :dogs should_belong_to :lover end |
Helpers
Finally, Shoulda adds a few basic-but-useful utility methods and assertions:
1 2 3 4 5 6 7 8 9 |
assert_difference(User, :count, 1) { User.create } assert_difference(User.packages, :size, 3, true) do User.add_three_packages end assert_contains(['a', '1'], /\d/) assert_same_elements([:a, :b, :c], [:c, :a, :b]) |
Why not rSpec or Test::Unit
A lot of people are going to be wondering why we didn’t just go with one of the many new BDD style testing frameworks that are out there, like Simply BDD, Test::Spec, or the wildly popular rSpec. I don’t want to minimize the work that’s been done on these very cool pieces of software – in fact, Shoulda would never have been written without their example to lead us. But there were some parts of the rest of the plugins that we just didn’t feel right about.
I wrote another post, specifically about rSpec, but here are the major points (most of which apply to all of the plugins):
contextdefined on Kernel, andshoulddefined on Object – This can make it very hard to extend the framework, and honestly just feels wrong. I can imagine that there would be method definition conflicts between the testing framework and the code being tested. I could be paranoid here, but I didn’t want to find out for sure months down the line.- Extendibility – I think the real value of this testing plugin is in the focused testing macros, which can test large chunks of common code in a single line. The issue above made these kinds of macros hard to write in the other testing frameworks.
- Mocking framework built-in – soon rSpec won’t suffer from this as much as it does now, but I still don’t like the idea of the testing framework and mocking framework being so interlocked.
lambda {}.should.differ(Blog, :count, 1)– This is purely a matter of taste, but we really didn’t see value in the ‘x.should y’ syntax over the ‘assert’ syntax.- Compatibility – This one is rSpec specific. As much as I like the format of the rSpec output, it can cause problems with automated programs that are expecting Test::Unit output. Also, having to run both ‘rake spec; rake test’ to see all of your tests pass seems like an unnecessary annoyance.
Docs & Installation
Full RDocs are available here, and the official Shoulda page is here.
We’re having some troubles with our plugin repository working withscript/plugin at the moment, so svn access will have to suffice:
1 2 |
svn ls http://svn.thoughtbot.com/plugins/shoulda/tags/ svn export http://svn.thoughtbot.com/plugins/shoulda/tags/rel-2.0.4 vendor/plugins/shoulda |
Comments on this post
Apr 06
court3nay said,
Heh. Haven’t you heard of test/spec?
http://chneukirchen.org/blog/archive/2006/10/announcing-test-spec-0-2-a-bdd-interface-for-test-unit.html
http://poocs.net/2007/4/3/test-spec-on-rails-and-assert_difference
http://weblog.techno-weenie.net/2006/11/24/test-spec-kicks-simply_bdds-ass
Apr 06
Tammer Saleh said,
Hey court3nay,
We looked very closely at test/spec, and almost decided to go with that. There were two things that it, and rSpec, did that we didn’t really agree with:
It defines context and should on Kernel and Object, respectively, which didn’t feel very clean, and (with rSpec, at least) makes it very hard to extend.
It uses the “x.should.equal 3” syntax. This is just a subjective thing, but we didn’t really find that as useful as the regular assert syntax. The lambda example in the assert_difference post you linked to is one in particular that I felt was less clean that just using assert_difference. Same with lambda {}.should.raise Exception. Again, it’s just a matter of taste, and some find that syntax to be more readable.
Apr 06
Tammer Saleh said,
btw: I really loved your tongue-in-cheek icons.
Apr 08
Daniel Fischer said,
Great! Thanks for the plugin, I can see this being put into exceptional use.
Apr 09
Morgan Roderick said,
Looks very interesting!
For those of us using acts_as_authenticated / RESTful authentication, won’t assert_difference clash with the assert_difference defined in AuthenticatedTestHelper?
Or, would we be limited to using just one of the test helpers at a given time, or even worse, do really long prefixes?
Apr 10
Rob Sanheim said,
Looks cool. The macro level test methods look like they’d be worth extracting to their own plugin.
re: test/spec – you can still use traditional asserts_foos—its still just a testcase, after all.
The test_spec on rails extensions that Rick has done are another good reason to go w/ test/spec over alternatives, but hey.
Can you discuss how you test wrt ActiveRecord? All mocks, dynamic fixtures, etc?
Apr 20
Tammer Saleh said,
Morgan:
I’ve never used the acts_as_authenticated plugin, so I hadn’t realized that it defined these methods. There’s been about a dozen implementations of assert_difference floating around on the net, so I’m not too surprised. I’m not entirely sure, but I would expect that whichever plugin is loaded last would override the definition of the other, so things should work just fine. Let me know if you actually experience problems with it.
Rob:
I would have liked to extract this plugin into three different ones, but they all actually make use of each other.
Though rSpec and test/spec can use the regular
assertcalls, there were the other issues I had outlined as well.Sorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Come “ride the toad” on Hoptoad, the app error app.
Thunder Thimble: Brand monitoring for social media.
Widgetfinger: Simple content management for simple websites.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our beginning or advanced training.
Archives