Created
February 8, 2011 07:45
-
-
Save knzai/816049 to your computer and use it in GitHub Desktop.
A pattern for testing class methods in ruby with rspec explicit subjects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# RSpec's subject method, both implicitly and explicitly set, is useful for | |
# declaratively setting up the context of the object under test. If you provide a | |
# class for your describe block, subject will implicitly be set to a new instance | |
# of this class (with no arguments passed to the constructor). If you want | |
# something more complex done, such as setting arguments, you can use the | |
# explicit subject setter, which takes a block. | |
describe Person do | |
context "born 19 years ago" do | |
subject { Person.new(:birthdate => 19.years.ago } | |
it { should be_eligible_to_vote } | |
its(:age) { should == 19 } | |
it "should be younger than a 20 year old" do | |
twenty_year_old = Person.new(:birthday => 20.years.ago) | |
subject.should be_younger_than(twenty_year_old) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The subject block is called after any befores. In fact, it is not called until | |
# it is referenced in the spec - the compact syntaxes of 'it { should }' and | |
# 'its(:property)' implicitly call subject as needed*. This means any method | |
# calls in the subject block reference the state of the #system (like your db), | |
# at the time subject is called. | |
describe "the oldest person" do | |
subject { Person.new(:birthdate => Person.oldest_person.birthdate - 1) } | |
it "should be older than the next oldest person" do | |
original_oldest_person = Person.new(:birthdate => 100.years.ago) | |
subject.should be_older_than(original_oldest_person) | |
end | |
end | |
# *The latter syntax actual resets subject, the getter, to be the result of the | |
# property access, allowing for the intriguing abuse of syntax: 'its(:property) { | |
# subject.should....' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# subject thus provides a decent syntax for testing method calls directly. | |
# Sometimes, though, you have methods you want to test almost purely based on | |
# what is passed into them, with few other concerns of state, for instance, when | |
# testing class methods. These specs may have a little shared set-up needed, but | |
# really the true object under test is the method call itself. In these cases it | |
# makes sense to set that as the subject, and this leads to an effective and | |
# succinct, if slightly distasteful, pattern for setting the method arguments via | |
# standard rspec ivar trickery. | |
describe ".first" do | |
before { Person.create_10_people } | |
subject { Person.first(@number_of_people) } | |
it "should return the specified number of people" do | |
@number_of_people = 5 | |
subject.size.should == 5 | |
end | |
it "should return a single person when so specified" do | |
@number_of_people = 1 | |
subject.should be_a(Person) | |
end | |
it "should default to returning a single person" do | |
subject.should be_a(Person) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I was bemoaning the difference between ruby and more functional languages like | |
# JavaScript, where you do have ways of easily differentiating between a method | |
# reference and call (parentheses), and how that would make a pattern like this | |
# easier by letting you set the method as the subject and then call it in the | |
# spec. Then I realized, of course ruby has a way for creating a callable | |
# reference like this: a lambda. | |
describe ".first" do | |
subject { lambda { |number_of_people| Person.first(number_of_people ) } } | |
it { subject.call(5).size.should == 5 } | |
it { subject.call(1).should be_a(Person) } | |
it { subject.call.should be_a(Person) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If I continue to like this pattern enough after more usage I'll probably see if I | |
can get them to accept a patch to allow passing of arguments directly into | |
subject itself. That could possibly get a little messy in the code for subject | |
itself, but behind the scenes gymnastics to get a nice testing syntax is what | |
rspec is all about, after all. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, I had forgotten about this. I would rework #3 to use let(), of course.