Posts Tagged testing

Testing relationships in Rails

Ler este artigo em português

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
 
    <span style="text-decoration: line-through;">@comment.should equal(post)</span>
 
    @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.

Tags: ,

Testando relacionamentos no Rails

Read this acticle in English

Estive procurando por uma maneira elegante de testar relacionamentos utilizando Rails, e achei interessante como pouca gente está preocupada com isso. Na maioria das vezes, dizem simplesmente que isto é função do próprio framework. Tudo bem, o has_many e belongs_to (esqueça que existe o HBTM) já foram devidamente testados, mas é importante saber que o que cabe a você testar o que você escreve no seu código. Portanto, se você quer ter certeza que a linha has_many :comments realmente existe, você deve escrever um teste para ela. Então, resolvi listar algumas abordagens:

1 – Utilizando fixtures:

No começo, pensei em utilizar as fixtures, mas usando relacionamentos dentro delas. É uma solução simples, porém traz um revés. Quando utilizamos um relacionamento dentro das fixtures, e este relacionamento não está explicitado nos models, será retornado um erro, ao invés de uma falha. Por exemplo:

# 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

Como não foi explicitamente dito que Comment belongs_to :post, ao se executar a fixture comments.yml será retornado um erro. Além disso, o relacionamento contrário, Post has_many :comments não será testado.

2 – Novamente utilizando fixtures:

Depois, encontrei este artigo, que também utiliza fixtures com uma abordagem diferente (e mais Rails 1.x) que é definir apenas a chave estrangeira na fixture. Assim, não seria retornado erro.
É funcional, mas eu quero apenas testar se as linhas de relacionamento estão definidas nos models. O resto, fica por conta do ActiveRecord. Além disso, não quero usar fixtures como se fossem testes, já que, primeiro, não são documentadas automaticamente (como no caso dos métodos de teste) e porque, caso você mude a estrutura da sua tabela, poderá ter seus testes quebrados por causa das fixtures que não refletem as mudanças.

3 – A opção errada que funciona:

Acabei utilizando a solução mais lógica, e baseada neste post do 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"

    post = Post.new

    @comment.post = post

    @comment.should equal(post)

    @comment.post.should equal(post)

  end

end

É simples, mas… por que é errada? Quando fazemos “@post.comments << comment”, estamos chamando o método comments do objeto @post. Como não foi declarado que Post has_many :comments, este método não existirá. Ela funciona porque o rSpec não acusará o erro, e apenas retornará uma falha no teste.
Ainda assim, prefiro essa solução porque eu não precisarei lidar com fixtures.

Tags: ,