7 coisas que você precisa conhecer no RSpec
11 de dezembro de 2009 - 09:42
O RSpec é um framework bastante completo e, por isso mesmo, muitas coisas são desconhecidas por grande parte dos desenvolvedores. Neste artigo, você conhecerá 7 coisas que irão mudar a maneira como você utiliza o RSpec.
Subject
O RSpec possui um método muito útil chamado subject, que retorna uma instância da classe que está sendo utilizada como contexto do exemplo.
describe User do it { should_not be_admin }
end A grande vantagem desta abordagem é que você aumenta consideravelmente a legibilidade de suas especificações. Mas você não precisa utilizar apenas o contexto especificado no método describe; se quiser, você pode definir o seu próprio!
describe User do subject { User.new(:admin => true) } it { should be_admin }
end Quando outros tipos de objetos (como módulos ou strings) são passados ao método describe, ele irá retornar o próprio objeto.
Message expectation
O método stub_chain
Sempre que você precisar fazer uma chamada encadeada e quiser verificar o último valor, utilize o método stub_chain. Ele permite transformar um bloco como este
describe "Mocks" do before do @user = mock() @things = mock() @recent = mock() @user.stub!(:things).and_return(@things) @things.stub!(:recent).and_return(@recent) @recent.should_receive(:count).and_return(100) end it "should return the recent things count from an user" do @user.things.recent.count.should == 100 end end
em uma coisa bem mais simples como esta
describe "Mocks" do before do @user = mock() @user.stub_chain(:things, :recent, :count).and_return(100) end it "should return the recent things count from an user" do @user.things.recent.count.should == 100 end end
O método and_return
Você alguma vez precisou acessar um stub mais de uma vez e queria que ele retornasse diferentes valores em cada chamada? Veja este exemplo.
require "open-uri" class Dice API_URL = "http://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new" def roll open(API_URL).to_i end end
Toda vez que o método roll for chamado, ele irá fazer uma requisição diferente ao http://random.org. Imagine que por algum motivo você precisasse retornar diferentes valores toda vez que o método open você invocado. Tudo o que você precisa fazer é retornar mais de um valor com o método and_return.
describe Dice do before do subject.should_receive(:open).and_return("1", "4", "2") end
it "should not cache request" do subject.roll.should == 1 subject.roll.should == 4 subject.roll.should == 2 end
end O método with
O RSpec disponibiliza uma série de métodos que podem ser usados em conjunto com o método with. Veja alguns exemplos:
module Echo extend self
def echo(*args) args.inspect end
end
describe Echo do # with any kind of argument specify("anything") { subject.should_receive(:echo).with(anything) subject.echo(1) }
# with hash containing values specify("hash_including") { subject.should_receive(:echo).with(hash_including(:say => "hello")) subject.echo(:say => "hello") }
# with an instance of String specify("instance_of") { subject.should_receive(:echo).with(instance_of(String)) subject.echo("hello") }
# with an object that responds to some methods specify("duck_type") { subject.should_receive(:echo).twice.with(duck_type(:<<)) subject.echo(Array.new) subject.echo(String.new) }
end Para ver mais exemplos, acesse https://gist.github.com/45b97f90a83c8acf3f29.
before e after globais
Muitas vezes precisamos preparar nosso ambiente antes de executarmos nossos testes. Quando isso precisa ser feito em mais de uma especificação, você pode diminuir a duplicação de código utilizando blocos globais.
No seu arquivo spec_helper.rb, você pode utilizar os métodos append_before, append_after_, prepend_before e prepend_after.
Spec::Runner.configure do |config| config.prepend_after { `rm -rf some/path` }
end Pending
Em vez de comentar temporariamente exemplos que você não quer que sejam executados, você pode utilizar o método pending. O método pending pode ser utilizado nos blocos de before. Assim, todos os exemplos daquele contexto de describe serão automaticamente marcados como pendente.
describe "Pending examples" do before do pending end # lots of examples end
Você também pode deixar apenas um exemplo pendente.
describe "Pending examples" do it "should be pending" do pending "need to working on it" Env.setup! end end
Ou apenas uma parte dele.
describe "Pending examples" do it "should be pending with a block" do pending("need to working on it as well") do Env.setup! end end
end Uma outra alternativa (que não é deixar como pendente mas sim desabilitado) é utilizar o método xit em vez de it.
describe "Pending examples" do xit "should be pending with alias" do Env.setup! end end
Matchers personalizados
Sempre que você perceber que está repetindo um padrão na hora de escrever seus exemplos, automatize o processo criando novos matchers. O RSpec possui duas maneiras diferentes de fazer isso. A mais simples delas é utilizando o método simple_matcher. Crie um módulo chamado TheAnswerMatchers para adicionarmos novos matchers.
module TheAnswerMatchers def be_the_answer simple_matcher do |given, matcher| # save some typing the_answer = "the Answer to Life, the Universe, and Everything"
matcher.description = "be #{the_answer}" matcher.failure_message = "expected #{given.inspect} to be #{the_answer}" matcher.negative_failure_message = "expected #{given.inspect} not to be #{the_answer}" given == 42 end end
end Agora, basta fazer com que o RSpec reconheça esses novos matchers; é só incluir o módulo.
Spec::Runner.configure do |config| config.include TheAnswerMatchers end
E para escrever seus exemplos, você pode usar o matcher be_the_answer:
describe "The Answer to Life, the Universe, and Everything" do specify { 42.should be_the_answer } specify { "the wrong answer".should_not be_the_answer }
end Finalizando…
Como você pode perceber, é bem simples escrever exemplos melhores no RSpec. Se você olhar as specs do RSpec, encontrará bastante coisa legal que você normalmente não veria (porque tem muita coisa para ler) no RDocs.
E você, tem alguma dica para compartilhar?










Comentários (0)