ruby - rails - RSpec permitir/esperar vs simplemente esperar/y_retorno
ruby capybara github (1)
En RSpec, específicamente la versión> = 3, hay alguna diferencia entre:
- El uso de
allow
configurar las expectativas del mensaje con parámetros que devuelven el doble de la prueba, y luego el usoexpect
hacer una afirmación sobre el doble de la prueba devuelta - Solo con
expect
a configurar la expectativa con los parámetros y devolver la prueba doble
¿O es todo sólo semántica? Sé que proporcionar / especificar un valor de retorno con expect
fue la sintaxis en RSpec mocks 2.13 , pero por lo que puedo ver, la sintaxis cambió en RSpec mocks 3 para usar permitida .
Sin embargo, en el código de ejemplo (que pasa) a continuación, el uso de allow
/ expect
o simplemente expect
/ and_return
parece generar el mismo resultado. Si se hubiera favorecido una sintaxis sobre otra, tal vez hubiera esperado que hubiera algún tipo de aviso de desaprobación, pero como no lo hay, parece que ambas sintaxis se consideran válidas:
class Foo
def self.bar(baz)
# not important what happens to baz parameter
# only important that it is passed in
new
end
def qux
# perform some action
end
end
class SomethingThatCallsFoo
def some_long_process(baz)
# do some processing
Foo.bar(baz).qux
# do other processing
end
end
describe SomethingThatCallsFoo do
let(:foo_caller) { SomethingThatCallsFoo.new }
describe ''#some_long_process'' do
let(:foobar_result) { double(''foobar_result'') }
let(:baz) { double(''baz'') }
context ''using allow/expect'' do
before do
allow(Foo).to receive(:bar).with(baz).and_return(foobar_result)
end
it ''calls qux method on result of Foo.bar(baz)'' do
expect(foobar_result).to receive(:qux)
foo_caller.some_long_process(baz)
end
end
context ''using expect/and_return'' do
it ''calls qux method on result of Foo.bar(baz)'' do
expect(Foo).to receive(:bar).with(baz).and_return(foobar_result)
expect(foobar_result).to receive(:qux)
foo_caller.some_long_process(baz)
end
end
end
end
Si deliberadamente hago que las pruebas fracasen al cambiar el parámetro baz
pasado en la expectativa a un doble de prueba diferente, los errores son más o menos iguales:
1) SomethingThatCallsFoo#some_long_process using allow/expect calls quux method on result of Foo.bar(baz)
Failure/Error: Foo.bar(baz).qux
<Foo (class)> received :bar with unexpected arguments
expected: (#<RSpec::Mocks::Double:0x3fe97a0127fc @name="baz">)
got: (#<RSpec::Mocks::Double:0x3fe97998540c @name=nil>)
Please stub a default value first if message might be received with other args as well.
# ./foo_test.rb:16:in `some_long_process''
# ./foo_test.rb:35:in `block (4 levels) in <top (required)>''
2) SomethingThatCallsFoo#some_long_process using expect/and_return calls quux method on result of Foo.bar(baz)
Failure/Error: Foo.bar(baz).qux
<Foo (class)> received :bar with unexpected arguments
expected: (#<RSpec::Mocks::Double:0x3fe979935fd8 @name="baz">)
got: (#<RSpec::Mocks::Double:0x3fe979cc5c0c @name=nil>)
# ./foo_test.rb:16:in `some_long_process''
# ./foo_test.rb:43:in `block (4 levels) in <top (required)>''
Entonces, ¿hay diferencias reales entre estas dos pruebas, ya sea en el resultado o la intención expresada, o es solo una semántica y / o preferencia personal? ¿Debería allow
/ expect
usar sobre expect
/ y and_return
en general, ya que parece que es la sintaxis de reemplazo, o cada uno de ellos está destinado a usarse en escenarios de prueba específicos?
Actualizar
Después de leer las respuestas de Mori , comenté la línea Foo.bar(baz).qux
del código de ejemplo anterior y obtuve los siguientes errores:
1) SomethingThatCallsFoo#some_long_process using allow/expect calls qux method on result of Foo.bar(baz)
Failure/Error: expect(foobar_result).to receive(:qux)
(Double "foobar_result").qux(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
# ./foo_test.rb:34:in `block (4 levels) in <top (required)>''
2) SomethingThatCallsFoo#some_long_process using expect/and_return calls qux method on result of Foo.bar(baz)
Failure/Error: expect(Foo).to receive(:bar).with(baz).and_return(foobar_result)
(<Foo (class)>).bar(#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">)
expected: 1 time with arguments: (#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">)
received: 0 times
# ./foo_test.rb:41:in `block (4 levels) in <top (required)>''
- La especificación de
foobar_result
falla porque el doblefoobar_result
nunca llega a representar el resultado deFoo.bar(baz)
, y por lo tanto nunca se ha llamado a#qux
- La especificación de
expect
falla en el momento en queFoo
nunca recibe.bar(baz)
por lo que ni siquiera llegamos al punto de interrogar el doblefoobar_result
Tiene sentido: no es solo un cambio de sintaxis, y que expect
/ and_return
tiene un propósito diferente de allow
/ expect
. Realmente debería haber comprobado el lugar más obvio: el RSpec Mocks README , específicamente las siguientes secciones:
Vea el clásico artículo Mocks Aren''t Stubs . allow
hace un trozo, mientras que expect
hace una burla. Es decir, allow
que un objeto devuelva X en lugar de lo que devolvería sin doblar, y expect
es un permiso más una expectativa de algún estado o evento. Cuando escribes
allow(Foo).to receive(:bar).with(baz).and_return(foobar_result)
... le estás diciendo al entorno de especificaciones que modifique Foo
para devolver foobar_result
cuando recibe :bar
con baz
. Pero cuando escribes
expect(Foo).to receive(:bar).with(baz).and_return(foobar_result)
... estás haciendo lo mismo, además de decirle a la especificación que falle a menos que Foo
reciba :bar
con baz
.
Para ver la diferencia, pruebe los dos ejemplos en los que Foo
no recibe :bar
con baz
.