O que você esperava que fosse acontecer?
Quando eu crio um mock em uma classe e tento controlar o comportamento de uma de suas funções usando withArgs
, uma exceção é lançada. Acontece apenas ao usar várias chamadas de withArgs
.
O que realmente acontece
Como reproduzirExemplo reproduzível
Suponha que eu declare uma classe C
:
class C {
static foo(arg1, arg2) {return 'foo';}
}
Tento controlar o comportamento de foo
usando uma simulação em C
:
it('test withArgs mock', () => {
let CMock = sinon.mock(C);
let fooStub = CMock.expects('foo');
fooStub.withArgs('a', 'b').returns(1);
fooStub.withArgs('c', 'd').returns(2);
expect(fooStub('a', 'b')).toEqual(1);
expect(fooStub('c', 'd')).toEqual(2);
CMock.restore();
});
O que lança a seguinte exceção:
ExpectationError: foo recebeu argumentos errados ["a", "b"], esperado ["c", "d"]
em Object.fail (node_modules / sinon / lib / sinon / mock-expectation.js: 281: 25)
na função.(node_modules / sinon / lib / sinon / mock-expectation.js: 182: 33)
em Array.forEach (nativo)
em Function.verifyCallAllowed (node_modules / sinon / lib / sinon / mock-expectation.js: 175: 27)
em Function.invoke (node_modules / sinon / lib / sinon / mock-expectation.js: 78: 14)
no proxy (node_modules / sinon / lib / sinon / spy.js: 89: 22)
Enquanto este código funciona:
it('test withArgs stub', () => {
let fooStub = sinon.stub(C, 'foo');
fooStub.withArgs('a', 'b').returns(1);
fooStub.withArgs('c', 'd').returns(2);
expect(fooStub('a', 'b')).toEqual(1);
expect(fooStub('c', 'd')).toEqual(2);
fooStub.restore();
});
Vale ressaltar que os seguintes cenários funcionam conforme o esperado:
it('test one withArgs mock', () => {
let CMock = sinon.mock(C);
let fooStub = CMock.expects('foo');
fooStub.withArgs('a', 'b').returns(1);
expect(fooStub('a', 'b')).toEqual(1); // ok
CMock.restore();
});
it('test one withArgs mock', () => {
let CMock = sinon.mock(C);
let fooStub = CMock.expects('foo');
fooStub.withArgs('a', 'b').returns(1);
expect(fooStub('c', 'd')).toEqual(1); // error: foo received wrong arguments ["c", "d"], expected ["a", "b"]
CMock.restore();
});
Ou seja, o problema ocorre apenas ao usar withArgs
várias vezes.
Pode estar relacionado ao # 1381, mas como você pode ver no exemplo, a solução sugerida não funciona.
Obrigado por um relatório questão exemplar. Eu não uso a funcionalidade de simulação, pois faz meu cérebro doer, então alguém precisará dar uma olhada nisso, mas seus exemplos devem tornar isso muito mais fácil.
Sinta-se à vontade para dar uma olhada no código - geralmente a maneira mais rápida de consertar isso :-)
Eu dei uma olhada no código.
Em primeiro lugar, este teste funciona:
class C {
static foo(arg1, arg2) {return 'foo';}
}
it('test withArgs mock', () => {
let CMock = sinon.mock(C);
CMock.expects('foo').withArgs('a', 'b').returns(1);
CMock.expects('foo').withArgs('c', 'd').returns(2);
expect(C.foo('a', 'b')).toEqual(1);
expect(C.foo('c', 'd')).toEqual(2);
CMock.restore();
});
Existem dois motivos pelos quais isso funciona e o teste do problema original não:
expects
.expects
.Ou seja, isso também não funciona:
it('test withArgs mock', () => {
let CMock = sinon.mock(C);
CMock.expects('foo').withArgs('a', 'b').returns(1);
let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);
expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
expect(fooStub('c', 'd')).toEqual(2);
CMock.restore();
});
Por que isso acontece?
Cada vez que expects
é chamado, acontece o seguinte:
minCalls/maxCalls
- que são as variáveis que são alteradas quando você chama once()
por exemplo) é criada, apenas que é um "stub com escopo", não tem conexão com outro expects
. É o objeto retornado de expects
.O que resulta em comportamentos diferentes para esses cenários:
expects
, ele será limitado apenas a expects
.expects
e encontrará um que se encaixe. Se houver mais de um, ele chamará o primeiro.Este é o comportamento correto? Parece-me mais intuitivo que uma vez que alguém chame expects
em um método, ele se comportará exatamente como stub
(com o minCalls/maxCalls
). Isso resultará em:
stub
e mock-expectation
.Agora, o que aconteceria com minCalls/maxCalls
?
Como há apenas um expectation
agora, proponho três maneiras alternativas de fazer a verificação da contagem de chamadas com simulações:
once()
, twice()
etc. let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.
Pode ser um pouco confuso.
once()
, twice()
etc. CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments.
CMock.expects('foo').withArgs('c', 'd').returns(2).once();
Mais claro, mas você não pode stub foo
mais de uma vez, portanto, precisaremos encontrar uma maneira de contornar isso. Além disso, isso ainda é possível:
CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
Este problema foi marcado automaticamente como obsoleto porque não teve atividades recentes. Ele será fechado se nenhuma outra atividade ocorrer. Obrigado por suas contribuições.
Comentários muito úteis
Eu dei uma olhada no código.
Em primeiro lugar, este teste funciona:
Existem dois motivos pelos quais isso funciona e o teste do problema original não:
expects
.expects
.Ou seja, isso também não funciona:
Por que isso acontece?
Cada vez que
expects
é chamado, acontece o seguinte:minCalls/maxCalls
- que são as variáveis que são alteradas quando você chamaonce()
por exemplo) é criada, apenas que é um "stub com escopo", não tem conexão com outroexpects
. É o objeto retornado deexpects
.O que resulta em comportamentos diferentes para esses cenários:
expects
, ele será limitado apenas aexpects
.expects
e encontrará um que se encaixe. Se houver mais de um, ele chamará o primeiro.Este é o comportamento correto? Parece-me mais intuitivo que uma vez que alguém chame
expects
em um método, ele se comportará exatamente comostub
(com ominCalls/maxCalls
). Isso resultará em:stub
emock-expectation
.Agora, o que aconteceria com
minCalls/maxCalls
?Como há apenas um
expectation
agora, proponho três maneiras alternativas de fazer a verificação da contagem de chamadas com simulações:once()
,twice()
etc.Pode ser um pouco confuso.
once()
,twice()
etc.Mais claro, mas você não pode stub
foo
mais de uma vez, portanto, precisaremos encontrar uma maneira de contornar isso. Além disso, isso ainda é possível: