Dans RSpec, en particulier la version> = 3, y a-t-il une différence entre:
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ésexpect
pour configurer l'attente avec des paramètres et renvoyer le test doubleou 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)>'
allow
échoue car le double foobar_result
Ne remplace jamais le résultat de Foo.bar(baz)
, et donc #qux
N'est jamais appelé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:
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
.