I am making a concerted effort to wrap my head around Rspec in order to move towards more of a TDD/BDD development pattern. However, I'm a long way off and struggling with some of the fundamentals:
Like, when exactly should I be using mocks/stubs and when shouldn't I?
Take for example this scenario: I have a Site
model that has_many :blogs
and the Blog
model has_many :articles
. In my Site
model I have a callback filter that creates a default set of blogs and articles for every new site. I want to test that code, so here goes:
describe Site, "when created" do
include SiteSpecHelper
before(:each) do
@site = Site.create valid_site_attributes
end
it "should have 2 blogs" do
@site.should have(2).blogs
end
it "should have 1 main blog article" do
@site.blogs.find_by_slug("main").should have(1).articles
end
it "should have 2 secondary blog articles" do
@site.blogs.find_by_slug("secondary").should have(2).articles
end
end
Now, if I run that test, everything passes. However, it's also pretty slow as it's creating a new Site, two new Blogs and three new Articles - for every single test! So I wonder, is this a good candidate for using stubs? Let's give it a go:
describe Site, "when created" do
include SiteSpecHelper
before(:each) do
site = Site.new
@blog = Blog.new
@article = Article.new
Site.stub!(:create).and_return(site)
Blog.stub!(:create).and_return(@blog)
Article.stub!(:create).and_return(@article)
@site = Site.create valid_site_attributes
end
it "should have 2 blogs" do
@site.stub!(:blogs).and_return([@blog, @blog])
@site.should have(2).blogs
end
it "should have 1 main blog article" do
@blog.stub!(:articles).and_return([@article])
@site.stub_chain(:blogs, :find_by_slug).with("main").and_return(@blog)
@site.blogs.find_by_slug("main").should have(1).articles
end
it "should have 2 secondary blog articles" do
@blog.stub!(:articles).and_return([@article, @article])
@site.stub_chain(:blogs, :find_by_slug).with("secondary").and_return(@blog)
@site.blogs.find_by_slug("secondary").should have(2).articles
end
end
Now all the tests still pass, and things are a bit speedier too. But, I've doubled the length of my tests and the whole exercise just strikes me as utterly pointless, because I'm no longer testing my code, I'm just testing my tests.
Now, either I've completely missed the point of mocks/stubs, or I'm approaching it fundamentally wrong, but I'm hoping someone might be able to either:
- Improve me tests above so it uses stubs or mocks in a way that actually tests my code, rather than my tests.
- Or, tell me if I should even be using stubs here - or whether in fact this is completely unnecessary and I should be writing these models to the test database.