Mocks e stubs são conceitos, muitas vezes de difícil compreensão, principalmente para desenvolvedores iniciantes em testes automatizados. Eu mesmo levei um tempinho para compreendê-los e nem sei se entendi muito bem. Por trás disso existe uma nomenclatura um tanto confusa e que não ajuda muito na compreensão dos conceitos. Vamos analisar como o Rspec trabalha com eles utilizando a biblioteca rspec-mocks.
Muitas vezes quando estamos escrevendo nossos testes, precisamos utilizar de objetos falsos. Em determinados cenários, o objeto real pode não estar disponível ou é necessário utilizar algum recurso externo que pode deixar o teste lento. Algumas vezes não precisamos testar o estado do objeto mas sim seu comportamento. Chamamos esses objetos falsos de Test Doubles ou Dublês de teste. Mocks e stubs são dois dos tipos especializados de doubles e são os mais utilizados.
Stubs
Simulam implementações dos objetos reais através de métodos que retornam valores pré-determinados. São métodos pré-configurados com as informações necessárias para a execução dos testes. Abaixo temos um exemplo de declaração de um test double.
[code language=\\\”ruby\\\”]
double = double(\\\’user\\\’, name: \\\’Tyrion Lanister\\\’)
[/code]
O primeiro parâmetro é uma descrição do dublê que é usada na documentação e nas mensagens de falha. O segundo é o método stub setado com um valor qualquer. Vamos a um exemplo melhor. Queremos testar o método calculate_total_price da classe Order.
[code language=\\\”ruby\\\”]
class Order
attr_reader :total
def calculate_total_price itens
@total = 0
itens.each { |item| @total += item.price }
end
end
[/code]
Podemos testar esse método sem instanciar o objeto real Item, através de stubs.
[code language=\\\”ruby\\\”]
describe Order, "#calculate_total_price" do
let(:itens) { [double(price: 10.0), double(price: 45.5)] }
it "calculates the total price" do
order = Order.new
order.calculate_total_price(itens)
expect(order.total).to eq(55.5)
end
end
[/code]
Na linha 3 setamos um array de doubles ao invés de uma array de objetos da classe Item. O teste irá passar. Isso é muito útil em testes de unidade,, pois não precisamos acessar o banco de dados para pegar os dados do objeto Item que poderia ser um modelo do Active Record.
Mocks
São doubles pré-programados que irão criar expectativas que deverão ser satisfeitas pelos testes. Podemos usá-los quando queremos testar a chamada de uma api externa, por exemplo. Vamos dar uma olhada no exemplo abaixo.
[code language=\\\”ruby\\\”]
class Westeros
def geolocate(place)
Geocoder.coordinates(place)
end
end
[/code]
O método geolocate da classe Westeros tem uma chamada da bilblioteca geocoder que retorna as coordenadas geográficas de um dado endereço. Precisamos testar se o método coordinates de Geocoder irá mesmo retornar as coordenadas? Acho que não. Os desenvolvedores da gem já fizeram isso. Precisamos mesmo é garantir que o método geolocate de Westeros irá chamar coordinates de Geocoder com o parâmetro correto. Para isso vamos usar um mock.
[code language=\\\”ruby\\\”]
describe Westeros, "#geolocate" do
it "retuns coordinates" do
place = \\\’Ponta Tempestade, Westeros\\\’
westeros = Westeros.new
expect(Geocoder).to receive(:coordinates).with(place)
westeros.geolocate(place)
end
end
[/code]
Na linha 7 definimos as expectativas. Geocoder deve executar o método coordinates com place como argumento. O teste passa. Dessa forma testamos a interface de geocoder e não seu funcionamento interno. Um detalhe a parte é a dsl do rspec que deixa a leitura muito simples e agradável proporcionando um modo bastante elegante de se escrever testes.
conclusão
O rspec possui muitas outras inúmeras funcionalidades no que diz respeito a test doubles e testes regulares. A prática e a experiência irão melhorar ainda mais o entendimento dos conceitos de mocks e stubs, assim como os conceitos de testes automatizados. Existem uma infinidade de artigos sobre o assunto na internet. Vale a pena dar uma pesquisada no assunto.
Referências
https://github.com/rspec/rspec-mocks
http://www.infoq.com/br/articles/mocks-Arent-Stubs
Deixe um comentário