o estado interno do jogo deve mudar de :initial para :started. Você pode
especificar esse comportamento do seguinte modo:
describe Game do
describe "#start" do
it 'changes the game state from :initial to :started' do
game = Game.new
expect {
game.start
}.to change { game.state }.from(:initial).to(:started)
end
end
end
Ne exemplo acima usamos o Change Matcher em:
expect {
game.start
}.to change { game.state }.from(:initial).to(:started)
para verificar que o valor do estado do objeto game muda de :initial para
:started quando a ação game.start for executada.
44
Casa do Código
Capítulo 3. Introdução ao básico do RSpec
Como você pôde ver no exemplo acima, o Change Matcher pode ser usado
quando você quer verificar que a execução de um bloco de código causa uma mu-
dança de estado de um objeto.
Seguem mais alguns exemplos de uso do Change Matcher :
# verifica que ao rodar Counter.increment o valor de Counter.count
# é modificado em duas unidades
expect {
Counter.increment
}.to change { Counter.count }.by(2)
# verifica que ao tentar salvar um user com um atributo inválido
# o valor de User.count não é modificado
expect {
invalid_attributes = { name: nil }
user = User.new(invalid_attributes)
user.save
}.to_not change(User, :count)
# verifica que ao adicionar alguns jogadores ao objeto team, o valor
# de team.size é modificado por pelo menos uma unidade
expect {
team.add_players(some_players)
}.to change(team, :size).by_at_least(1)
Pense nesse tipo de matcher como um verificador que analisará o valor antes e
depois de alguma ação.
Outros matchers padrão do RSpec
Pronto, já vimos a maioria dos matchers padrão que vem com o RSpec. Mas,
como são muitos e muitas variações, não vale a pena ver todos os detalhes de todos
os matchers que vem com o RSpec de uma vez só. Por isso convido você a depois dar
uma boa navegada pela documentação do RSpec para ver mais sobre os matchers
padrão.
Você pode ver essa documentação de duas formas, no Relish (https://www.
relishapp.com/rspec/rspec-expectations/v/2-14/docs/built-in-matchers) , que é uma
documentação mais alto nível. Ou, você pode ver direto no código do RSpec, no
seguinte link do Github: https://github.com/rspec/rspec-expectations/blob/v2.13.0/
lib/rspec/matchers.rb.
45
3.7. Custom matchers
Casa do Código
Vale a pena guardar esses links, pois junto com o que vimos neste livro, eles são
uma ótima referência
sobre os matchers padrão do RSpec.
Agora que você já leu bastante sobre os matchers que vem com o RSpec, descanse
um pouquinho, pois a seguir você irá aprender a escrever os seus próprios matchers!
3.7
Custom matchers
Na seção anterior nós aprendemos sobre os matchers padrão do RSpec e vimos que
existem muitos deles. Mas mesmo com o RSpec nos oferecendo tantos matchers,
as vezes podemos precisar de mais. As vezes temos que escrever nossos próprios
matchers. Vamos ver quando isso pode ser necessário.
Imagine que você está trabalhando em uma aplicação de e-commerce. Você está
encarregado de trabalhar na funcionalidade de categorização de produtos. O escopo
da funcionalidade diz que:
uma categoria contém uma ou mais subcategorias;
uma subcategoria contém um ou mais produtos;
uma categoria contém todos os produtos contidos por suas subcategorias.
Vamos pensar num exemplo para o escopo acima. Na nossa aplicação pode exis-
tir uma categoria chamada eletrônicos. Essa categoria pode conter duas subcate-
gorias, tais como computadores e celulares. A subcategoria computadores pode
conter o produto MacBook e a subcategoria celulares pode conter o produto iPhone.
Logo, a categoria eletrônicos deve conter os produtos MacBook e iPhone.
Até então você já tem o código de duas classes. O código da classe Category:
class Category
attr_reader :subcategories
attr_reader :name
def initialize(name)
@name = name
@subcategories = []
end
def add_subcategories(*subcategories)
# TODO
46
Casa do Código
Capítulo 3. Introdução ao básico do RSpec
end
end
E o código da classe Subcategory:
class Subcategory
attr_reader :products
def initialize(name)
@name = name
@products = []
end
def add_product(product)
@products << product
end
end
Agora imagine que você precise escrever um teste unitário para especificar que
uma categoria contém todos os produtos de suas subcategorias. Poderíamos começar
escrevendo esse teste do seguinte modo:
describe Category do
it "contains all the products of its subcategories"
end
No setup desse teste vamos criar um objeto para a categoria "eletronics",
criar objetos para as subcategorias "computers" e "cell phones" e adicionar um produto para cada subcategoria:
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")
end
end
Agora podemos adicionar essas subcategorias a categoria "eletronics" e ve-