sar para ele o nome do método que ele vai definir. Quando você usa o subject desse
modo, você está definindo o setup do SUT, mas sem dar um nome para ele. Para usar
o subject definido desse modo, basta você chamar o método subject de dentro do
teste, como fizemos acima:
subject.publish!
Depois de analisar esses detalhes, perceba que esse teste nada mais é do que a
generalização dos testes que fizemos antes para as classes BlogPost e Paper.
Para fazer esse teste passar, basta escrevermos o código do módulo
Publishable:
module Publishable
attr_reader :published_on
def publish!
today = Time.now.strftime("%Y-%m-%d")
@published_on = today
end
end
Agora que já temos esse módulo pronto, podemos inclui-lo nas classes
BlogPost e Paper:
class BlogPost
include Publishable
end
class Paper
include Publishable
end
Com isso conseguimos reduzir a duplicação nas classes em si, mas e a duplicação
nos testes? Assim como conseguimos extrair o comportamento duplicado nas nossas
classes para um módulo, podemos extrair os testes duplicados para o que o RSpec
chama de shared examples.
Para fazer isso, vamos começar transformando os testes que fizemos acima para
o módulo Publishable em shared examples. Para definir um conjunto de shared
examples você deve usar o método shared_examples_for e passar para ele uma
string nomeando esse conjunto de examples:
82
Casa do Código
Capítulo 4. Organização, refatoração e reuso de testes com o RSpec
shared_examples_for "a publishable object" do
end
Agora basta definir quais são os examples que pertencem a esses shared examples
passando eles dentro do bloco do shared_examples_for:
shared_examples_for "a publishable object" do
describe "#publish" do
it "saves the date when the object is published" do
subject.publish!
today = Time.now.strftime("%Y-%m-%d")
expect(subject.published_on).to eq(today)
end
end
end
Para usar um conjunto de shared examples, você deve usar o método
it_behaves_like ou o método include_examples passando como argu-
mento o nome dos shared examples. Ambos os métodos são bem parecidos e logo
logo vamos ver a diferença entre eles. Agora vamos re-escrever os testes do módulo
Publishable utilizando os shared examples que criamos acima:
class PublishableObject
include Publishable
end
describe "A published object" do
subject { PublishableObject.new }
include_examples "a publishable object"
end
Pronto, isso é tudo que é necessário para utilizarmos os shared examples que de-
finimos. Perceba que além de incluir os shared examples, nesse caso ainda foi neces-
sário definir o subject. Isso porque os shared examples estão usando o subject
definido pelo teste que os inclui:
shared_examples_for "a publishable object" do
describe "#publish" do
it "saves the date when the object is published" do
83
4.6. Reuso de testes
Casa do Código
subject.publish! # uso do subject definido pelo teste que incluir
# estes shared examples
# (...)
end
end
end
Na verdade, os shared examples tem acesso a tudo que é definido dentro do teste
que os inclui, assim como um módulo tem acesso aos métodos da classe que o inclui.
Para finalizar a refatoração dos testes do módulo Publishable, vamos rodar
o teste e ver como ficou o output do RSpec. Assumindo que esses testes estejam em
um arquivo chamado shared_examples_spec.rb, podemos rodá-lo do seguinte
modo:
$ rspec --format documentation shared_examples_spec.rb
A publishable object
#publish
saves the publication date
Perceba
como os testes que definimos nos shared examples "a publishable
object" foram incluidos diretamente no output do example group que fizemos para
testar o módulo Publishable.
Bom, agora só falta utilizarmos esses shared examples nos testes das classes
BlogPost e Paper. Fazer isso é bem simples, basta você utilizar o método
it_behaves_like:
describe BlogPost do
it_behaves_like "a publishable object"
end
describe Paper do
it_behaves_like "a publishable object"
end
Perceba que para os testes das classes acima não foi necessário definirmos ex-
plicitamente o subject, como fizemos no teste do módulo Publishable. Isso
porque quando você não define explicitamente o subject, o RSpec faz isso para
84
Casa do Código
Capítulo 4. Organização, refatoração e reuso de testes com o RSpec
você automaticamente. Ele define o subject automaticamente quando o argu-
mento passado para o método describe é uma classe. Então quando escrevemos
o seguinte código:
describe BlogPost do
end
O RSpec na verdade define o subject de modo implícito do seguinte modo:
describe BlogPost do
subject { BlogPost.new }
end
Apesar do RSpec definir o subject de modo implícito, sempre dê preferência para
definir o subject de modo explícito e dando um nome para ele. Ao fazer desse modo
você deixará seu teste mais claro.
Por fim, vamos rodar os testes das classes BlogPost e Paper para ver que
o método it_behaves_like gera um output um pouco diferente do método
include_examples:
$ rspec --format documentation shared_examples_spec.rb