rspec: ¿Cómo tropezar con un método de instancia llamado por el constructor?
Pregunta
class A
def initialize
@x = do_something
end
def do_something
42
end
end
¿Cómo puedo colocar do_something
en rspec, antes de que se llame a la implementación original (asignando 42 a @x
)? Y sin cambiar la implementación, por supuesto.
Solución
Aquí está el commit que agrega la función a rspec - Este fue el 25 de mayo de 2008. Con esto puedes hacer
A.any_instance.stub(do_something: 23)
Sin embargo, la última versión de gema de rspec (1.1.11, octubre de 2008) no tiene este parche.
Este ticket indica que lo retiraron por razones de mantenimiento, y aún no se ha proporcionado una solución alternativa.
No parece que puedas hacerlo en este momento. Tendrás que hackear la clase manualmente usando alias_method o somesuch.
Otros consejos
He encontrado esta solución en 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
No sé cómo hacerlo en el marco simulado de las especificaciones, pero puedes cambiarlo fácilmente por mocha para hacer lo siguiente:
# 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
o con RR :
stub.any_instance_of(A).do_something { 23 }
En la última versión de RSpec a partir de hoy - 3.5 puede:
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
Me gusta la respuesta de Denis Barushev. Y me gustaría sugerir solo un cambio cosmético que hace innecesaria la variable new_method
. Rspec analiza los métodos apagados, para que se pueda acceder con el prefijo proxied_by_rspec__
:
A.stub!(:new).and_return do |*args|
a = A.proxied_by_rspec__new(*args)
a.should_receive(:do_something).and_return(23)
a
end
En RSpec 2.6 o posterior puede usar
A.any_instance.stub(do_something: 23)
Ver los documentos para más detalles. (Gracias a rogerdpack por señalar que esto ahora es posible, pensé que merecía una respuesta propia)
Para tropezar con un método de instancia, puede hacer algo como esto:
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
Pero como señaló pschneider, solo devuelve 42 en nuevo con:
A.stub (: new) .and_return (42)
o algo por el estilo.
Aquí hay una idea que podría no ser muy elegante, pero que seguramente funcionará:
Cree una clase pequeña que herede la clase que desea probar, anule el método de inicialización y llame a super
después de haber creado los apéndices en la inicialización, así:
it "should call during_init in initialize" do
class TestClass < TheClassToTest
def initialize
should_receive(:during_init)
super
end
end
TestClass.new
end
¡Y ahí tienes! Acabo de usar esto con éxito en una de mis pruebas.
La gema rspec_candy viene con un método auxiliar stub_any_instance
que funciona en ambos RSpec 1 y RSpec 2.
En mi versión de rspec (1.2.2) puedo hacer esto:
A.should_receive(:new).and_return(42)
Sé que probablemente sea demasiado tarde para responder al póster original, pero lo contesto de todos modos para referencia futura, ya que vine aquí con la misma pregunta pero descubrí que estaba funcionando en la última versión de rspec.