This is the last article in this series describing the concept of double-blind test-driven development. This style of testing can add time to development, but this can be cut significantly using RSpec matchers.
If you’re not familiar with matchers, they’re the helpers that give RSpec its english-like syntax, and they can be a powerful tool speeding up all of your test-driven development – whether you follow the double-blind method or not.
If you’re using RSpec, you’re already using their built-in matchers. Say we have a Site model, and its url method takes the host attribute and appends the ‘http://’ protocol. Here’s a likely test:
describe Site, 'url'
it "should begin with http://" do
site = Site.new :host => 'example.com'
site.url.should equal('http://example.com')
end
end
The equal() method in the code above is the matcher. You can pass it to any of RSpec’s should or should_not methods, and it will magically work.
But the magic isn’t that hard, and you can harness it yourself for custom matchers that conform to your application.
The Many Faces of Custom RSpec Matchers
While I don’t want this article to turn into a primer on custom RSpec matchers (it’s a little off-topic), I’ll give you the three styles of defining them, and explain my recommendations. There are simple matchers, the Matcher DSL, and full RSpec matcher classes.
Let’s start by writing a test we want to run:
it "should be at least 5" do 6.should be_at_least(5) end
This test should always pass, provided we’ve defined our matcher correctly. The first way to do this is the simple matcher:
def be_at_least(minimum)
simple_matcher("at least #{minimum}"){|actual| actual >= minimum}
end
As you might guess, actual represents the object that “.should” whatever – in this case “.should be_at_least(5)”. This version makes a lot of assumptions, including the auto-creation of generic pass and fail messages.
If you want a little more control, you can step up to RSpec’s Matcher DSL. This is the middle-of-the-road option for creating custom matchers:
RSpec::Matchers.define :be_at_least do |minimum|
match do |actual|
actual >= minimum
end
failure_message_for_should do |actual|
"expected #{actual} to be at least #{minimum}"
end
failure_message_for_should_not do |actual|
"expected #{actual} to be less than #{minimum}"
end
description do
"be at least #{minimum}"
end
end
Now we’re rocking custom failure messages, and test names. This is pretty cool, and honestly how I started out doing matchers. It’s also how I started out doing the matchers for double-blind testing.
The problem is that by skipping the creation of actual matcher classes, we lose the ability to do things like inheritance. Not a big deal if our matchers stay simple, but they won’t. Not if we use them as often as we should! I found myself re-defining the same helper methods in each matcher I defined this way.
So let’s see just how daunting a full-fledged custom matcher class really is:
module CustomMatcher
class BeAtLeast
def initialize(minimum)
@minimum = minimum
end
def matches?(actual)
@actual = actual
@actual >= @minimum
end
def failure_message_for_should
"expected #{@actual} to be at least #{@minimum}"
end
def failure_message_for_should_not
"expected #{@actual} to be less than #{@minimum}"
end
end
def be_at_least(expected)
BeAtLeast.new(expected)
end
end
This isn’t so bad! We’re defining a new class, but you can see it doesn’t have to inherit from anything, or use any unholy Ruby voodoo to work.
We just have to define four methods: initialize, match? (which returns true or false), and the two failure message methods. Along the way, we set some instance variables so we can access the data when we need it. Finally, we define a method that creates a new instance of this class, and that’s what RSpec will rely on.
You can add as many other methods as these four will rely on. But you also get other benefits over the DSL. You can use inheritance, moving common methods up the chain so you only have to define them once, instead of in each matcher definition. You can also write setup/teardown code in your parent classes, make default arguments a breeze, and standardize any error handling. I do all of these in the matchers I created for the example app.
The bottom line is this: defining your own matcher classes directly really DRY’s up your matchers, and that always makes life simpler. I think it’s the only way to go for serious and heavy RSpec users. It allows the class for my validate_presence_of matcher to be this short and sweet:
module DoubleBlindMatchers
class ValidatePresenceOf < ValidationMatcher
def default_options
{:message => "can't be blank", :with => 'x'}
end
def match
set_to @options[:with]
@object.valid?
check !@object.errors[@attribute].include?(@options[:message]), shouldnt_exist
set_to nil
check !@object.valid?, valid_when('nil')
check @object.errors[@attribute].include?(@options[:message])
set_to ""
check !@object.valid?, valid_when("blank")
check @object.errors[@attribute].include?(@options[:message])
end
end
def validate_presence_of expected, options = {}
ValidatePresenceOf.new expected, options
end
end
And the Teacher model, which grew considerably during our double-blind testing, now looks like this (in its entirety):
# spec/models/teacher_spec.rb
require 'spec_helper'
describe Teacher do
it {should have_many :subjects}
it {should validate_presence_of :name}
it {should validate_length_of :name, :maximum => 50, :message => "must be 50 characters or less"}
it {should validate_presence_of :salary}
it {should validate_numericality_of :salary, :within => (20_000..100_000), :message => "must be between $20K and $100K"}
end
Summary
Now that you’ve seen my entire proposal for double-blind testing, let me know what you think. Be cruel if you must, it’s the only way I’ll learn. I’ll do the best to explain (not defend) my reasoning, and keep an open mind to changes.
I’ll also be publishing my double-blind matchers as a gem so you can add them to your project.




Nested Comments in Ruby on Rails, part 2: Controllers and Views
January 26, 2011Part 1 of this series came out exactly 3 months and 3 days ago. Special thanks to a reader named Edward who prodded me to finally add the controllers and views to this.
Going beyond the model layer for nested comments introduces a new programming idiom: recursion. Some ruby developers may not be familiar with it – especially if your experience is mostly web-related, where the need doesn’t come up as often. Recursion in a nutshell is the act of a method calling itself. If you’ve seen Inception, The ability to have dreams within dreams within dreams means those dreams are recursive. If you haven’t seen the movie, think of russian matryoshka dolls. You won’t experience star-studded special effects with the dolls, but you’ll at least get the idea of recursion.
Unlike russian dolls or most of Leo’s recent work, recursion in software is potentially infinite. Practically speaking though, it’s more like the doll thing. After all, a system only has so many resources, and recursion is expensive in this regard – the method must copy itself in memory at each layer, local variables and all. On the plus side, they tend to be lightning fast compared to standard iteration using loops. And in our case, we’ll be hitting the database at each layer. We’ll ignore the dangers in our simple app, though.
Routing
Let’s start with our routing file:
# config/routes.rb NestedComments::Application.routes.draw do resources :comments do resources :comments end resources :posts do resources :comments end root :to => 'posts#index' endWorking backward, we’re making our Posts controller’s index action our default route. That’s just to get the app functional. Next comes something interesting: nesting our comments inside of our posts. Interesting, but boring. Finally, the main event: nesting our comments within our comments!
Before you get too excited and start pulling out your Nana’s childhood russian doll set for comparision, this isn’t true recursion. It’s well documented that nesting resources any more than two layers deep is painful and unnecessary, so think of this as the lamest russian doll ever.
Controllers
First, our Posts controller, which is less exciting:
# app/controllers/posts_controller.rb class PostsController < ApplicationController def index @posts = Post.all end def show @post = Post.find(params[:id]) end def new @post = Post.new end def create @post = Post.new(params[:post]) if @post.save redirect_to posts_path, :notice => "Your post was created successfully." else render :action => :new end end endWe’re setting up a pretty standard restful resource here, with a couple actions skipped for simplicity. Now the comments controller (get those dolls ready):
# app/controllers/comments_controller.rb class CommentsController < ApplicationController before_filter :get_parent def new @comment = @parent.comments.build end def create @comment = @parent.comments.build(params[:comment]) if @comment.save redirect_to post_path(@comment.post), :notice => 'Thank you for your comment!' else render :new end end protected def get_parent @parent = Post.find_by_id(params[:post_id]) if params[:post_id] @parent = Comment.find_by_id(params[:comment_id]) if params[:comment_id] redirect_to root_path unless defined?(@parent) end endIt’s not much bigger, but there’s a lot going on here! First, since comments are nested, we have to look for a parent. We’re only creating comments in this example, so we only have those related actions. Comments will always be shown on a post page.
The really exciting part is after a successful comment creation. How do we redirect back to the post page? For all we know, this comment could buried down 12 layers of replies. All we really have access to so far is the parent of the object. This necessitates a new model method:
Recursive functions are often short and sweet for two reasons: they’re already complex by nature, and adding more code than necessary would make them unmanageable. Also, they’re getting a lot done in just a few lines. In this case, the second line is the key: if “commentable” (the parent object) is a post, return that. Otherwise, call this same method on the parent, which will in turn check if *it* is a Post, and so on.
I could have written it shorter, like this:
In fact, I did at first. But the extra code that checks and sets an instance variable is caching the result. This way, if we call the same method on an object more than once, it stores the result for future use. Remember, recursion can be expensive – especially when the database is involved.
Views
Finally, it’s view time, with one more bit of recursion for fun.
Or post views are standard scaffolding mostly, with the exception of the show view:
Notice we have the partial app/views/comments/_comment.html.erb. We’re calling this for each of our post’s comments. Nothing too fancy here. Now, for the partial itself:
# app/views/comments/_comment.html.erb <li class="comment"> <h3><%= comment.title %></h3> <div class="body"> <%= comment.body %> </div> <p><%= link_to 'Add a Reply', new_comment_comment_path(comment) %></p> <% unless comment.comments.empty? %> <ul class="comment_list"> <%= render :partial => 'comments/comment', :collection => comment.comments %> </ul> <% end %> </li>This partial is recursive! The comments controller doesn’t have a show method, because we’re never going to view a comment by itself. Instead, the show-like code is in this partial, and at the end it checks to see if *this* comment has comments. If so, it calls the partial again on the whole collection. The end result is a nested, bulleted list of comments. This is not very sexy if you fire up the code yourself, but it’s a great starting point.
Summary
Hopefully this article as done a good job of explaining both recursion, and how to use it to achieve nested comments in your applications. If you’re new to recursion as a concept, haven’t seen Inception, didn’t inherit russian dolls from Nana or receive them as a snazzy graduation present, and my explanation somehow fell short, it’s a well documented programming idiom. There are tons of resources online, so take the time to learn this powerful tool, then learn not to overuse it :)
Please download the code and play with it if you want to learn more – the code is fully test-driven so you can see how that works, which is just as important.
On a final note, I’m tempted to do a follow-up article with ajax and some nicer formatting. Perhaps in 3 months and 3 days…
Tags:comments, nesting, polymorphism, posts, Rails, recursion, recursive, Ruby, ruby on rails
Posted in Database, Rails, Testing | 9 Comments »