Capítulo 3. Introdução ao básico do RSpec
eletronics_products = eletronics.subcategories.map { |sub|
sub.products
}
eletronics_products.flatten!
expect(eletronics_products).to include("MacBook", "iPhone")
end
end
Queremos modificá-lo usando um custom matcher para ficar
assim:
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)
expect(eletronics).to contain_products("MacBook", "iPhone")
end
end
Para escrever esse custom matcher, iremos mais uma vez utilizar a DSL do RSpec
e extrair a lógica de verificação original para dentro desse matcher:
RSpec::Matchers.define :contain_products do |*products|
match do |category|
subcategories_products = category.subcategories.map { |sub|
sub.products
}
subcategories_products.flatten!
expect(subcategories_products & products).to eq products
end
end
Agora que temos o custom matcher pronto, podemos utilizá-lo no nosso teste
para ficar assim:
describe Category do
it "contains all the products of its subcategories" do
51
3.7. Custom matchers
Casa do Código
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)
expect(eletronics).to contain_products("MacBook", "iPhone")
end
end
Repare que após essa refatoração de extrair uma lógica de verificação complexa
para um custom matcher, o comportamento esperado no nosso teste ficou bem mais
claro.
Outro ponto que ficou melhor é a mensagem de erro do nosso teste. Para vermos
isso, basta rodarmos esse teste e ver a seguinte mensagem:
Failures:
1) Category contains all the products of its subcategories
Failure/Error: expect(eletronics).to
contain_products("MacBook", "iPhone")
expected #<Category:0x007fcb1c0fc460 @name="eletronics",
@subcategories=[]>
to contain products "MacBook" and "iPhone"
Na mensagem de erro já está mais claro que o teste falhou porque a categoria
eletronics não contém os produtos "MacBook" e "iPhone", mas ainda podemos melhorar essa mensagem ainda mais.
A mensagem de erro acima foi gerada automaticamente pelo RSpec a par-
tir do nome do nosso custom matcher.
Como nem sempre a mensagem pa-
drão fica clara, é possível customizá-la. Podemos fazer isso usando o método
failure_message_for_should da DSL de custom matcher do RSpec:
failure_message_for_should do |category|
"expected category #{category.name} to contain products #{products}"
end
O código acima deve ser adicionado dentro da definição do nosso custom mat-
cher:
52
Casa do Código
Capítulo 3. Introdução ao básico do RSpec
RSpec::Matchers.define :contain_products do |*products|
match do |category|
subcategories_products = category.subcategories.map { |sub|
sub.products
}
subcategories_products.flatten!
expect(subcategories_products & products).to eq products
end
failure_message_for_should do |category|
"expected category #{category.name} to contain products #{products}"
end
end
Agora, ao rodarmos o teste, ele falha com a seguinte mensagem:
Failures:
1) Category contains all the products of its subcategories
Failure/Error: expect(eletronics).to
contain_products("MacBook", "iPhone")
expected category eletronics to contain products ["MacBook", "iPhone"]
Após a customização, a mensagem de erro ficou bem mais clara!
Além dessa opção de customização, o RSpec tem várias outras. Como são muitas,
convido você a olhar a documentação de custom matchers do RSpec (https://www.
relishapp.com/rspec/rspec-expectations/v/2-14/docs/custom-matchers) para ver as
outras opções de customização.
Agora que você já teve a experiência de construir um custom matcher com a
DSL do RSpec, vamos ver melhor o que de fato é um matcher pro RSpec e descobrir
o protocolo de matchers por trás disso tudo.
3.8
Entendendo o protocolo interno de matcher do
RSpec
Na seção anterior nós aprendemos a construir nossos próprios matchers pro RSpec
usando a DSL de custom matchers. Essa DSL server para facilitar a criação de novos
53
3.8. Entendendo o protocolo interno de matcher do RSpec
Casa do Código
matchers, mas ela não é o único modo de criar um matcher novo. Você pode criar seu
próprio matcher se entender o protocolo por trás da relação entre uma expectation
e o seu matcher.
Como vimos na seção 3.1, uma expectation segue a seguinte estrutura básica:
expect(actual).to matcher(expected)
Para ficar mais claro como uma expectation é executada, vamos colocar mais
alguns parênteses nessa estrutura:
expect(actual).to(matcher(expected))
Você pode ver pela linha de código acima que o matcher utilizado não é nada
mais do que um argumento para o método to. O contrato entre esse método e o