Ruby on Rails 2.1 - Teste de Unidade

Introdução

Antes de prosseguir, vamos dar uma olhada rápida em algumas definições -

  • o Tests- São aplicativos de teste que produzem resultados consistentes e provam que um aplicativo Rails faz o que se espera que faça. Os testes são desenvolvidos simultaneamente com o aplicativo real.

  • o Assertion- Esta é uma linha de código que avalia um objeto (ou expressão) quanto aos resultados esperados. Por exemplo - este valor é = aquele valor? Este objeto é nulo?

  • o Test Case- Esta é uma classe herdada de Test :: Unit :: TestCase contendo uma estratégia de teste composta por testes relacionados ao contexto.

  • o Test Suite- Esta é uma coleção de casos de teste. Quando você executa um conjunto de testes, ele, por sua vez, executa cada teste que pertence a ele.

Teste de Rails

Quando você executa o script helper script / generate para criar controladores e modelos , o Rails gera um framework para testes unitários e funcionais. Você pode obter uma cobertura de teste muito boa preenchendo a estrutura com testes para a funcionalidade que você escreve. Existem dois pontos importantes para testar em uma aplicação Rails -

  • Testando os Modelos

  • Testando os controladores

Este tutorial irá abranger ambos os testes resumidamente. Então, vamos criar um testapp para entender o conceito.

C:\ruby> rails -d mysql testapp

Configuração do banco de dados

Até agora, usamos apenas o banco de dados de desenvolvimento de aplicativos Rails, mas agora você precisa ter certeza de que o banco de dados de teste também foi criado e as seções apropriadas de seu arquivo config / database.yml estão configuradas corretamente.

Vamos criar bancos de dados de desenvolvimento e teste da seguinte maneira -

mysql> create database testapp_test;
Query OK, 1 row affected (0.01 sec)

mysql> create database testapp_development;
Query OK, 1 row affected (0.01 sec)

mysql> use testapp_test;
Database changed

mysql> grant all privileges on testapp_test.* 
   to 'root'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Configurando database.yml

Configure seu config / database.yml da seguinte maneira -

development:
   adapter: mysql
   encoding: utf8
   database: testapp_development
   username: root
   password: password
   host: localhost
test:
   adapter: mysql
   encoding: utf8
   database: testapp_test
   username: root
   password: password
   host: localhost
production:
   adapter: mysql
   encoding: utf8
   database: testapp_production
   username: root
   password: password
   host: localhost

Gerar Migração

Suponha que você tenha uma tabela contendo livros, incluindo seus títulos, preço e uma pequena descrição. A migração a seguir configura esta tabela -

testapp > ruby script/generate migration books

Agora modifique o arquivo testapp / db / migrate / 20080616170315_books.rb da seguinte maneira -

class Books < ActiveRecord::Migration
   def self.up
      create_table :books do |t|
         t.string     :title, :limit => 32, :null => false
         t.float      :price
         t.text       :description
         t.timestamp  :created_at
      end
   end
  
   def self.down
      drop_table :books
   end
end

Agora execute a migração da seguinte maneira -

testapp > rake db:migrate

Isso vai criar bookstabela no banco de dados testapp_development. Depois disso, precisamos configurar seu banco de dados de teste usando o comando rake da seguinte forma -

C:\ruby\testapp > rake db:test:clone_structure

Isso clonará o banco de dados testapp_development no banco de dados testapp_test . Isso significa que tudo o que você tem no banco de dados de desenvolvimento, agora também terá os mesmos dados no banco de dados de teste.

Modelos de teste

Quando você gera um modelo com o script de geração, o Rails também gera um script de teste de unidade para o modelo no diretório de teste. Ele também cria um fixture , um arquivo YAML contendo dados de teste a serem carregados no banco de dados testapp_test. Estes são os dados com os quais seus testes de unidade serão executados -

testapp > ruby script/generate model Book
   exists  app/models/
   exists  test/unit/
   exists  test/fixtures/
   create  app/models/book.rb
   create  test/unit/book_test.rb
   create  test/fixtures/books.yml
   create  db/migrate
   create  db/migrate/20080616164236_create_books.rb

Conforme você escreve o código nas classes de modelo, você escreverá testes correspondentes nesses arquivos. Então, vamos criar dois registros de livro de teste usando YAML em test / fixtures / books.yml da seguinte maneira -

perl_cb:
   id: 1
   title: 'Ruby Tutorial'
   price: 102.00
   description : 'This is a nice Ruby tutorial'
java_cb:
   id: 2
   title: 'Java Programming'
   price: 62.00
   description : 'Java Programming for the beginners'

Agora vamos substituir o código existente no arquivo de teste de unidade do livro test / unit / book_test.rb pelo seguinte código -

require File.dirname(__FILE__) + '/../test_helper'

class BookTest < ActiveSupport::TestCase
   fixtures :books

   def test_book

      perl_book = Book.new :title => books(:perl_cb).title, 
         :price => books(:perl_cb).price,
         :description => books(:perl_cb).description,
         :created_at => books(:perl_cb).created_at

      assert perl_book.save

      perl_book_copy = Book.find(perl_book.id)

      assert_equal perl_book.title, perl_book_copy.title

      perl_book.title = "Ruby Tutorial"

      assert perl_book.save
      assert perl_book.destroy
   end
end

Finalmente, execute o método de teste da seguinte maneira -

testapp > ruby test/unit/book_test.rb

Aqui está o resultado da execução do caso de teste de sucesso -

testapp > ruby test/unit/book_test_crud.rb 
Loaded suite ./test/unit/book_test
Started
.
Finished in 0.0625 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

Vamos analisar o que aconteceu aqui -

  • O método BookTest começa criando um novo objeto Book usando o título e outros campos do primeiro registro no texto fixture / books.yml. O objeto resultante é armazenado na variável de instância perl_book.

  • A primeira asserção testa se o salvamento do objeto Book foi bem-sucedido.

  • Em seguida, o objeto de livro é recuperado usando o método find e armazenado em outra variável de instância chamada perl_book_copy. O sucesso dessa recuperação é testado na próxima asserção, que compara os títulos de ambos os objetos de livro. Neste ponto, testamos a capacidade de criar e ler um registro de banco de dados.

  • A solução testa a atualização atribuindo um novo título ao objeto armazenado em perl_book e, a seguir, afirma que o salvamento da alteração foi bem-sucedido.

  • Finalmente, a capacidade de destruir um objeto Livro é testada.

É assim que podemos testar nossos modelos Rails.

Testando os controladores

O teste do controlador também é conhecido como functional testing. O teste funcional testa o seguinte tipo de funcionalidade dos controladores -

  • A resposta foi redirecionada conforme o esperado?
  • O modelo esperado é renderizado?
  • O roteamento está conforme o esperado?
  • A resposta contém as tags esperadas?

O framework Rails suporta cinco tipos de solicitações -

  • get
  • post
  • put
  • head
  • delete

Para escrever um teste funcional, você precisa simular qualquer um dos cinco tipos de solicitação HTTP que seu controlador processará.

Os tipos de solicitação "get" e "post" são os mais comumente usados ​​em testes de controlador. Todos esses métodos levam quatro argumentos -

  • A ação de um controlador
  • Um hash opcional de parâmetros de solicitação
  • Um hash de sessão opcional
  • Um hash flash opcional

Neste tutorial, veremos como usar getmétodo para testar nosso controlador. Você pode testar o restante dos métodos de maneira semelhante.

Quando você gera um controlador com generate, o Rails cria um script de teste funcional para o controlador da seguinte maneira -

testapp > ruby script/generate controller Book
   exists  app/controllers/
   exists  app/helpers/
   create  app/views/book
   exists  test/functional/
   create  app/controllers/book_controller.rb
   create  test/functional/book_controller_test.rb
   create  app/helpers/book_helper.rb

Conforme você escreve o código nas classes do controlador, você escreverá testes correspondentes nesses arquivos. Antes disso, vamos definir nossa lista de funções do controlador , mostrar e pesquisar dentroapp/controllers/book_controller.rb como segue -

class BookController < ApplicationController
   def list
      @book_pages, @books = paginate :books, :per_page => 10
   end

   def show
      @book = Book.find(params[:id])
   end

   def search
      @book = Book.find_by_title(params[:title])
      if @book
         redirect_to :action => 'show', :id => @book.id
      else    
         flash[:error] = 'No such book available'
         redirect_to :action => 'list'
      end
   end
end

NOTE - Você precisaria de dois modelos de visualizações para showe método de lista . Você pode definir essas visualizações e testá-las, mas agora, continuaremos sem definir essas visualizações.

Agora vamos reutilizar nosso dispositivo de teste que está no test/fixtures/books.yml arquivo da seguinte forma -

perl_cb:
   id: 1
   title: 'Ruby Tutorial'
   price: 102.00
   description : 'This is a nice Ruby tutorial'
java_cb:
  id: 2
  title: 'Java Programming'
  price: 62.00
  description : 'Java Programming for the beginners'

Adicione os seguintes test_search_book e test_search_not_found métodos para test / functional / book_controller_test.rb para testar a funcionalidade de ação de busca do Controlador do Livro.

require File.dirname(__FILE__) + '/../test_helper'
require 'book_controller'

# Re-raise errors caught by the controller.
class BookController
   def rescue_action(e) 
      raise e 
   end
end

class BookControllerTest < Test::Unit::TestCase
   fixtures :books
   def setup
      @controller = BookController.new
      @request    = ActionController::TestRequest.new
      @response   = ActionController::TestResponse.new
   end

   def test_search_book
      get :search, :title => 'Ruby Tutorial'
      assert_not_nil assigns(:book)
      assert_equal books(:perl_cb).title, assigns(:book).title
      assert_valid assigns(:book)
      assert_redirected_to :action => 'show'
   end

   def test_search_not_found
      get :search, :title => 'HTML Tutorial'
      assert_redirected_to :action => 'list'
      assert_equal 'No such book available', flash[:error]
   end
end

Agora execute seus casos de teste da seguinte maneira -

testapp > ruby test/functional/book_controller_test.rb

Ele fornece a seguinte saída -

Loaded suite test/functional/book_controller_test
Started
..
Finished in 0.422 seconds.

2 tests, 7 assertions, 0 failures, 0 errors

Vamos analisar o que aconteceu aqui -

  • O método de configuração é um método padrão para criar objetos de controlador, solicitação e resposta. Eles seriam usados ​​pelo Rails internamente.

  • O primeiro método de teste test_search_book gera umgetsolicitação para a ação de pesquisa, passando um parâmetro de título .

  • As próximas duas asserções verificam se um objeto Book foi salvo em uma variável de instância chamada @book e se o objeto passa por quaisquer validações Active Record que possam existir.

  • A asserção final dentro do primeiro método testa que a solicitação foi redirecionada para a ação show do controlador.

  • O segundo método de teste, test_search_not_found , executa outra solicitação get, mas passa um título inválido

  • As primeiras asserções testam se um redirecionamento para a ação list foi emitido.

  • Se as afirmações anteriores forem aprovadas, deve haver uma mensagem no hash flash que você pode testar com assert_equal ..

Para obter mais informações sobre Assertions, consulte a Documentação Padrão do Rails .

Usando Rake para Testes

Você pode usar rakeutilitário para testar seus aplicativos. Abaixo está uma lista de alguns comandos importantes.

  • $rake test - Teste todos os testes de unidade e testes funcionais (e testes de integração, se houver).

  • $rake test:functionals- Execute todos os testes funcionais.

  • $rake test:units - Execute todos os testes de unidade.

  • $rake test:integration - Execute todos os testes de integração.

  • $rake test:plugins - Execute todos os testes em ./vendor/plugins/**/test.

  • $rake test:recent - Executar testes para modelos e controladores que foram modificados nos últimos 10 minutos -

  • $rake test:uncommitted - Para projetos no Subversion, execute testes para as mudanças que ocorreram nos modelos e controladores desde o último commit -