rificar que os produtos delas estão contidos na categoria "eletronics":
47
3.7. Custom matchers
Casa do Código
describe Category do
it "contains all the products of its subcategories" do
eletronics = Category.new("eletronics")
computers
= Subcategory.new("computers")
cell_phones = Subcategory.new("cell phones")
computers.add_product("MacBook")
cell_phones.add_product("iPhone")
eletronics.add_subcategories(computers, cell_phones)
eletronics_products = eletronics.subcategories.map { |sub|
sub.products
}
eletronics_products.flatten!
expect(eletronics_products).to include("MacBook", "iPhone")
end
end
Perceba como a intenção da lógica de verificação desse teste não está 100% clara:
eletronics_products = eletronics.subcategories.map { |sub|
sub.products
}
eletronics_products.flatten!
expect(eletronics_products).to include("MacBook", "iPhone")
Só de bater o olho nesse código não da para dizer que a intenção dele é verificar
que a categoria "eletronics" contém os produtos "MacBook" e "iPhone".
Esse é o primeiro ponto a melhorar.
O segundo ponto a melhorar nós podemos identificar ao rodar o teste e ler sua
mensagem de erro:
1) Category contains all the products of its subcategories
Failure/Error:
expect(eletronics_products).to
include("MacBook", "iPhone")
expected [] to include "MacBook" and "iPhone"
A mensagem de erro não fala explicitamente que o erro é que a categoria
"eletronics" não contém nenhum dos produtos que ela deveria conter. Lembre-
se que a mensagem de erro de um teste é muito importante, pois uma mensagem de
erro clara pode nos ajudar a resolver mais rápido um teste que está quebrado.
48
Casa do Código
Capítulo 3. Introdução ao básico do RSpec
Então pelo menos esses dois pontos nós podemos melhorar no nosso teste, a
clareza da intenção da lógica de verificação e a mensagem de erro do teste. Para
fazer essa melhora podemos escrever um custom matcher do RSpec e utilizá-lo para
refatorar a verificação do nosso teste para ficar assim:
expect(eletronics).to contain_products("MacBook", "iPhone")
Vamos aprender a como fazer um custom matcher do RSpec.
Escrevendo um custom matcher do RSpec
O RSpec nos oferece uma DSL para escrever um custom matcher de modo bem
simples. Para entender essa DSL, vamos escrever um custom matcher que serve para
verificar se um número é múltiplo de 7. Esse matcher poderá ser usado do seguinte
modo:
expect(21).to be_a_multiple_of(7)
O primeiro passo para escrevermos o custom matcher acima é utilizar o método
RSpec::Matchers.define da DSL do RSpec:
RSpec::Matchers.define :be_a_multiple_of
O método define cria um método nomeado segundo o argumento que foi
passado para ele, no nosso caso o método be_a_multiple_of. Continuando a
seguir a DSL, é necessário passar um bloco para esse método:
RSpec::Matchers.define :be_a_multiple_of do |expected|
end
Repare no argumento que é passado para o bloco, o expected. Esse argumento
é o mesmo que é passado para o nosso matcher na hora que é utilizado. Logo, quando
utilizarmos be_a_multiple_of(7), o valor de expected será 7.
Para finalizar o nosso matcher, é necessário colocar a lógica de verificação dentro
dele, ou seja, a lógica para checar se um número é múltiplo de outro. Podemos fazer
isso checando se o resto da divisão entre o número testado e o argumento passado
para o matcher é zero:
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
(actual % expected) == 0
end
end
49
3.7. Custom matchers
Casa do Código
Repare que para implementar a lógica de verificação utilizamos o método
match. Chamamos esse método passando um bloco pra ele com um argumento,
o actual, que é o objeto sendo testado. No exemplo que estamos usando:
expect(21).to be_a_multiple_of(7)
actual é o 21 (objeto sendo testado) e o expected é o 7 (argumento passado
para o matcher).
Pronto, isso é o mínimo necessário para escrevermos nosso próprio matcher.
Com esse matcher pronto, poderíamos escrever um teste do seguinte modo:
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
(actual % expected) == 0
end
end
describe "The be_a_multiple_of custom matcher" do
it "can be used to verify if a number is a multiple of another one" do
expect(21).to be_a_multiple_of(7)
expect(15).to be_a_multiple_of(3)
expect(7).not_to be_a_multiple_of(3)
end
end
Agora que já sabemos como construir nosso próprio matcher, vamos voltar ao
nosso objetivo inicial, escrever um matcher para verificar que uma categoria contém
um ou mais produtos.
O teste que escrevemos para a categorização de produtos está até então do se-
guinte modo:
describe Category do
it "contains all the products of its subcategories" do
eletronics = Category.new("eletronics")
computers
= Subcategory.new("computers")
cell_phones = Subcategory.new("cell phones")
computers.add_product("MacBook")
cell_phones.add_product("iPhone")
eletronics.add_subcategories(computers, cell_phones)
50
Casa do Código