Test:unit no Rails

#automated-test, #dev, #ruby

Nota: Este post foi escrito para ser muito didático quanto aos testes

Quem usa Rails como ferramenta de desenvolvimento sabe que um dos assuntos mais falados é testes, não só aqueles programadores mais experientes, mas todos que entram nesse meio.

Visando isso comecei a aprender sobre testes e neste post vou mostrar como usar a biblioteca padrão do Rails para testes para fazer testes nos models.

Primeiro: criar a aplicação.

$ rails Test_example
      create  
      create  app/controllers
      create  app/helpers
      create  app/models
      [ … ]
$ cd Test_example

Façamos a migração, apesar de não termos nada:

$ rake db:migrate

Para iniciar o teste vamos primeiro verificar se existe o model post:

Adicione o seguinte conteúdo ao novo arquivo test/unit/post_test.rb:

require 'test_helper'

class PostTest < ActiveSupport::TestCase
  # Replace this with your real tests.
  test "exist model" do
    post = Post.new :title => "O título do meu primeiro post", :body => "O conteúdo do meu primeiro post"
    assert post.save
  end
end

Obviamente este teste falha, pois ainda não existe a classe (ou model) Post:

$ rake test:units
(in /home/dmitry/Projects/Test_example)
/usr/bin/ruby1.8 -I"/home/dmitry/Projects/Test_example/lib" -I"/home/dmitry/Projects/Test_example/test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/unit/post_test.rb" 
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
E
Finished in 0.014721 seconds.

  1) Error:
test_exist_model(PostTest):
NameError: uninitialized constant PostTest::Post
    /test/unit/post_test.rb:6:in `test_exist_model'

1 tests, 0 assertions, 0 failures, 1 errors
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I"/home/dmitry/Projects/...]

(See full trace by running task with --trace)

Para que o nosso teste passe, ou seja, para que ele realmente crie um post vamos gerar o model (e por conseguinte a migration) do post:

$ ./script/generate model Post title:string body:text
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/post.rb
overwrite test/unit/post_test.rb? (enter "h" for help) [Ynaqdh] n
        skip  test/unit/post_test.rb
      create  test/fixtures/posts.yml
      create  db/migrate
      create  db/migrate/20090628122701_create_posts.rb

Note que o script/generate questionou-me se queria sobrescrever o arquivo de test criado anteriormente por mim e a resposta não.

Agora vamos fazer a migration novamente:

$ rake db:migrate
(in /home/dmitry/Projects/Test_example)
==  CreatePosts: migrating ====================================================
-- create_table(:posts)
   -> 0.0015s
==  CreatePosts: migrated (0.0016s) ===========================================

Pronto agora nosso test irá passar!

$ rake test:units
(in /home/dmitry/Projects/Test_example)
/usr/bin/ruby1.8 -I"/home/dmitry/Projects/Test_example/lib" -I"/home/dmitry/Projects/Test_example/test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/unit/post_test.rb" 
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
.
Finished in 0.061828 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Ainda faltam dois testes:

  • Se o title (ou título) for vazio;
  • Se o body (ou o conteúdo do post propriamente dito) for vazio;

Vamos ao primeiro, modificando o arquivo test/unit/post_test.rb:

require 'test_helper'

class PostTest < ActiveSupport::TestCase
  # Replace this with your real tests.
  test "exist model" do
    post = Post.new :title => "O título do meu primeiro post", :body => "O conteúdo do meu primeiro post"
    assert post.save
  end

  test "verifica title" do
    post = Post.new :title => nil, :body => "O conteúdo do meu primeiro post"
    post.save
    assert_not_nil post.errors.on(:title)
  end
end

E mais uma vez o nosso teste falha: por que queremos que não seja possível a criação de um post sem título, porém ele fez essa criação:

$ rake test:units
(in /home/dmitry/Projects/Test_example)
/usr/bin/ruby1.8 -I"/home/dmitry/Projects/Test_example/lib" -I"/home/dmitry/Projects/Test_example/test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/unit/post_test.rb" 
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
.F
Finished in 0.062326 seconds.

  1) Failure:
test_verifica(PostTest) [/test/unit/post_test.rb:13]:
<false> is not true.

2 tests, 1 assertions, 1 failures, 0 errors
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I"/home/dmitry/Projects/...]

(See full trace by running task with --trace)

Vamos ao model corrigir isso:

class Post < ActiveRecord::Base
  validates_presence_of :title
end

Voltando ao teste:

ubuntu[pts/1]% rake test:units
(in /home/dmitry/Projects/Test_example)
/usr/bin/ruby1.8 -I"/home/dmitry/Projects/Test_example/lib" -I"/home/dmitry/Projects/Test_example/test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader.rb" "test/unit/post_test.rb" 
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake/rake_test_loader
Started
..
Finished in 0.038705 seconds.

2 tests, 2 assertions, 0 failures, 0 errors

Passou novamente!

Antes de proseguir com os testes gostaria de salientar a existencia da contagem de teste:

2 tests, 2 assertions, 0 failures, 0 errors

A primeira mostra o total de blocos de testes. A segunda a quantidade de testes (assert) que ocorreram conforme o esperado. O terceiro os testes que não passaram, também referentes ao `assert’. A última contagem mostra a quantidade de erros, ou seja, falhas mais grotescas, como por exemplo erro de sintaxe.

Antes de prosseguir novamente gostaria que vocês mesmos escrevessem os próximos testes como tarefa, o que foi feito aqui está em http://github.com/dmitrynix/Test_example.

Só uma dica: este post tem como objetivo os testes unitários, mas pode-se sim criar mais rapidamente as views (e o controller) usando o scaffold, basta apenas que você negue que seja sobrescrito os arquivos de teste e o model.

Posts nesta série