validates_presence_of rails example ruby testing rspec mocking rspec3

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 uso expect 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 doble foobar_result nunca llega a representar el resultado de Foo.bar(baz) , y por lo tanto nunca se ha llamado a #qux
  • La especificación de expect falla en el momento en que Foo nunca recibe .bar(baz) por lo que ni siquiera llegamos al punto de interrogar el doble foobar_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 .