web-dev-qa-db-fra.com

RSpec autoriser / attendre vs simplement attendre / et_retour

Dans RSpec, en particulier la version> = 3, y a-t-il une différence entre:

  • Utilisation de allow pour configurer les attentes de message avec des paramètres qui renvoient des doubles de test, puis utilisation de expect pour effectuer une assertion sur les doubles de test renvoyés
  • Il suffit d'utiliser expect pour configurer l'attente avec des paramètres et renvoyer le test double

ou est-ce juste de la sémantique? Je sais que fournir/spécifier une valeur de retour avec expect était la syntaxe dans RSpec mocks 2.1 , mais pour autant que je puisse voir, la syntaxe a changé dans RSpec mocks pour utiliser allow.

Cependant, dans l'exemple de code (passant) ci-dessous, l'utilisation de allow/expect ou simplement expect/and_return Semble générer le même résultat. Si une syntaxe était préférée à une autre, je me serais peut-être attendu à ce qu'il y ait une sorte d'avis de dépréciation, mais comme il n'y en a pas, il semblerait que les deux syntaxes soient considérées comme valides:

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 je fais délibérément échouer les tests en changeant le paramètre baz passé dans l'attente d'un autre test double, les erreurs sont à peu près les mêmes:

  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)>'

Alors, y a-t-il de réelles différences entre ces deux tests, que ce soit dans le résultat ou l'intention exprimée, ou s'agit-il simplement de sémantique et/ou de préférence personnelle? Est-ce que allow/expect doit être utilisé sur expect/and_return En général car il semble que ce soit la syntaxe de remplacement, ou est-ce que chacun d'eux est destiné à être utilisé dans des scénarios de test spécifiques?

Mise à jour

Après avoir lu la réponse de Mori , j'ai commenté la ligne Foo.bar(baz).qux de l'exemple de code ci-dessus et j'ai obtenu les erreurs suivantes:

  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 spécification allow échoue car le double foobar_result Ne remplace jamais le résultat de Foo.bar(baz), et donc #qux N'est jamais appelé
  • La spécification expect échoue au point de Foo de ne jamais recevoir .bar(baz) donc nous n'arrivons même pas à interroger le double foobar_result

C'est logique: ce n'est pas seulement un changement de syntaxe, et que expect/and_return A un but différent de allow/expect. J'aurais vraiment dû vérifier l'endroit le plus évident: le RSpec Mocks README , en particulier les sections suivantes:

30
Paul Fioravanti

Voir l'article classique Les moqueries ne sont pas des talons . allow crée un talon tandis que expect crée une maquette. C'est-à-dire que allow permet à un objet de renvoyer X au lieu de tout ce qu'il retournerait non tronqué, et expect est un allow plus une attente d'un état ou d'un événement. Quand tu écris

allow(Foo).to receive(:bar).with(baz).and_return(foobar_result)

... vous dites à l'environnement de spécifications de modifier Foo pour renvoyer foobar_result quand il reçoit :bar avec baz. Mais quand tu écris

expect(Foo).to receive(:bar).with(baz).and_return(foobar_result) 

... vous faites de même, en plus de dire l'échec de la spécification sauf si Foo reçoit :bar avec baz.

Pour voir la différence, essayez les deux dans les exemples où Foo ne ne reçoit pas :bar avec baz.

86
Mori