I’ve been looking for an elegant way to test relationships using Rais, and think it’s interesting how so few people really care about it. Most simply say that it’s up to the framework to test it’s own functions. Yeah, right, has_many and belongs_to have been tested before, but you should be aware that it’s up to you to check out the code you right. So, you have to make sure that a simple has_many :comments is really where it should be, so you should test it. Anyway, I listed some approaches I’ve found:
1 – Using fixtures:
At first, I thought about using fixtures, writing the relationships inside it (Rails 2.0). It’s a simple solution, but it has a downside. When we declare dependencies on the fixtures, and the relationship isn’t on the model, it’ll raise an error instead of a test failing. e.g.:
# post.rb class Post < ActiveRecord::Base end# comment.rb class Comment < ActiveRecord::Base end # posts.yml testing_relationships: title: Hello World body: Hello... # comments.yml commenting_relationships: body: I disagree! author: Filipe Coimbra post: testing_relationships
As I haven’t said that Comment belongs_to :post, when a test that calls comments.yml is called, an error will be raised. Besides, the other dependency, Post has_many :comments won’t be tested.
Besides, it’s not a cool thing to use fixtures as if they were tests. Besides, they’re not automatically documented.
2 – Again using fixtures:
After a while, I found this arcticle, which also uses fixtures, but with a different approach (a little bit Rails 1.2-ish) just defining the relation through the foreign key. That way, no error will happen.
It works, although you just have to test your own code, the rest is up to ActiveRecord. But, you may won’t like using fixtures because they may broke when you change your table structure.
3 – The wrong option that works:
Finally, I ended up using the most logical solution, that is writing ruby code, based on this post by Err The Blog.
# post_spec.rb
describe Post do
before(:each) do
@post = Post.new
end it "should be related to comments" do
comment = Comment.new(:body => "Testing relationships")
@post.comments << comment
@post.comments.should include(comment)
end
end
# comment_spec.rb
describe Comment do
before(:each) do
@comment = Comment.new
end
it "should be related to a post" do
post = Post.new
@comment.post = post
@comment.should equal(post)
@comment.post.should equal(post)
end
end
It’s simple and easy, but… why is it wrong? When I write “@post.comments << comment”, I’m calling a method comments on the object @post. As I didn’t declare that Post has_many :comments, this will be a missing method. Fortunately it works because rSpec won’t raise any errors, and just will tell us that the spec fails.
Even so, I still prefer this approach as I won’t be dealing with fixtures.