rspec: Como stub um método de instância chamado pelo construtor?
Pergunta
class A
def initialize
@x = do_something
end
def do_something
42
end
end
Como posso stub do_something
em rspec, antes da implementação original é chamado (atribuindo, assim, 42 a @x
)? E sem alterar a aplicação, é claro.
Solução
Aqui está a cometer o que adiciona o recurso para rspec - Este foi em 25 de maio de 2008. Com isso, você pode fazer
A.any_instance.stub(do_something: 23)
No entanto, a versão mais recente jóia de rspec (1.1.11, outubro de 2008) não tem este patch nele.
Este bilhete estados que puxou-o para fora por motivos de manutenção, e uma solução alternativa ainda não foi fornecido.
Não parece que você pode fazê-lo neste momento. Você vai ter que cortar a classe manualmente usando alias_method ou algo assim.
Outras dicas
Eu encontrei esta solução no http://pivotallabs.com/introducing-rr/
new_method = A.method(:new)
A.stub!(:new).and_return do |*args|
a = new_method.call(*args)
a.should_receive(:do_something).and_return(23)
a
end
Eu não sei como fazer isso no quadro de simulação de spec, mas você pode facilmente trocá-lo por mocha para fazer o seguinte:
# should probably be in spec/spec_helper.rb
Spec::Runner.configure do |config|
config.mock_with :mocha
end
describe A, " when initialized" do
it "should set x to 42" do
A.new.x.should == 42
end
end
describe A, " when do_something is mocked" do
it "should set x to 23" do
A.any_instance.expects(:do_something).returns(23)
A.new.x.should == 23
end
end
ou com RR :
stub.any_instance_of(A).do_something { 23 }
Na versão mais recente do RSpec a partir de hoje - 3.5 você pode:
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
Eu gosto de resposta Denis Barushev. E eu gostaria de sugerir apenas uma mudança cosmética que torna variável new_method
desnecessário. Rspec faz munge em métodos stubbed, para que eles possam ser acessados ??com proxied_by_rspec__
prefixo:
A.stub!(:new).and_return do |*args|
a = A.proxied_by_rspec__new(*args)
a.should_receive(:do_something).and_return(23)
a
end
Em RSpec 2.6 ou posterior você pode usar
A.any_instance.stub(do_something: 23)
Veja os docs para mais detalhes. (Graças a rogerdpack por apontar que isso agora é possível - eu pensei que merecia uma resposta do seu próprio)
Para stub um método de instância, você pode fazer algo como isto:
before :each do
@my_stub = stub("A")
@my_stub.should_receive(:do_something).with(no_args()).and_return(42)
@my_stub.should_receive(:do_something_else).with(any_args()).and_return(true)
A.stub(:new).and_return(my_stub)
end
Mas, como pschneider apontou, basta retornar 42 na nova com:
A.stub(:new).and_return(42)
ou algo nesse sentido.
Aqui está uma idéia que pode não ser muito elegante, mas é basicamente a certeza de trabalho:
Criar uma pequena classe que herda a classe que você deseja testar, substituir o método e chamada initialize super
depois ter criado os canhotos na inicialização, assim:
it "should call during_init in initialize" do
class TestClass < TheClassToTest
def initialize
should_receive(:during_init)
super
end
end
TestClass.new
end
E lá vai você! Eu apenas usei isso com sucesso em um dos meus testes.
O rspec_candy gem vem com um método auxiliar stub_any_instance
que funciona em ambos RSpec 1 e RSpec 2.
Na minha versão do rspec (1.2.2) eu posso fazer isso:
A.should_receive(:new).and_return(42)
Eu sei que é provavelmente tarde demais para responder ao autor, mas eu respondê-la de qualquer maneira para referência futura, como eu vim aqui com a mesma pergunta, mas figura para fora ele estava trabalhando na versão mais recente rspec.