How can I assert a function has `await`ed an async function using should.js
I have an async function f
that calls another async function g
. To test if f
calls g
, I'm stubbing g
using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await
when calling g
in f
.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then
is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
javascript node.js sinon should.js
add a comment |
I have an async function f
that calls another async function g
. To test if f
calls g
, I'm stubbing g
using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await
when calling g
in f
.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then
is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
javascript node.js sinon should.js
Doesshould-sinon
pull insinon
? Presumably you'd want torequire('sinon')
beforerequire('should-sinon')
?
– James
Nov 28 '18 at 23:32
add a comment |
I have an async function f
that calls another async function g
. To test if f
calls g
, I'm stubbing g
using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await
when calling g
in f
.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then
is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
javascript node.js sinon should.js
I have an async function f
that calls another async function g
. To test if f
calls g
, I'm stubbing g
using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await
when calling g
in f
.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then
is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
javascript node.js sinon should.js
javascript node.js sinon should.js
asked Nov 28 '18 at 23:04
snaksnak
3,21711326
3,21711326
Doesshould-sinon
pull insinon
? Presumably you'd want torequire('sinon')
beforerequire('should-sinon')
?
– James
Nov 28 '18 at 23:32
add a comment |
Doesshould-sinon
pull insinon
? Presumably you'd want torequire('sinon')
beforerequire('should-sinon')
?
– James
Nov 28 '18 at 23:32
Does
should-sinon
pull in sinon
? Presumably you'd want to require('sinon')
before require('should-sinon')
?– James
Nov 28 '18 at 23:32
Does
should-sinon
pull in sinon
? Presumably you'd want to require('sinon')
before require('should-sinon')
?– James
Nov 28 '18 at 23:32
add a comment |
2 Answers
2
active
oldest
votes
Your example for f
shows flawed code design which becomes more obvious if you write the same function without async/await
syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g
resolved becomes hard to tell (assuming you don't know if f
returned g
's promise, which is the same as not knowing whether f
awaited g
). If f
is not interested in the result of g
it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f
might have to trigger several async
calls sequentially await
ing several g_1
, g_2
,... to resolve, then you can build a test chain by asserting in the stub of g_n+1
that the dummy-promise of g_n
has been resolved. In general your approach to test a dummy-promise for its status is fine.
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned byf
to expose the value with whichg
resolves. Your example withthen
syntax is exactly how one might do this withoutasync/await
.
– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return whatg
returns. You might want to call severalg_1
,g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.
– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to whatg
actually does, and what of that operation is acceptable to expose throughf
. Certainly the test would be cleaner without having to track the completion status ofg
manually, as I do in my answer.
– sripberger
Dec 3 '18 at 17:19
add a comment |
Instead of stubbing then
, you're best off stubbing g
in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f
to make sure f
waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f
wait on any promise that waits at least as long as it takes for g
to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53529435%2fhow-can-i-assert-a-function-has-awaited-an-async-function-using-should-js%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Your example for f
shows flawed code design which becomes more obvious if you write the same function without async/await
syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g
resolved becomes hard to tell (assuming you don't know if f
returned g
's promise, which is the same as not knowing whether f
awaited g
). If f
is not interested in the result of g
it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f
might have to trigger several async
calls sequentially await
ing several g_1
, g_2
,... to resolve, then you can build a test chain by asserting in the stub of g_n+1
that the dummy-promise of g_n
has been resolved. In general your approach to test a dummy-promise for its status is fine.
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned byf
to expose the value with whichg
resolves. Your example withthen
syntax is exactly how one might do this withoutasync/await
.
– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return whatg
returns. You might want to call severalg_1
,g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.
– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to whatg
actually does, and what of that operation is acceptable to expose throughf
. Certainly the test would be cleaner without having to track the completion status ofg
manually, as I do in my answer.
– sripberger
Dec 3 '18 at 17:19
add a comment |
Your example for f
shows flawed code design which becomes more obvious if you write the same function without async/await
syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g
resolved becomes hard to tell (assuming you don't know if f
returned g
's promise, which is the same as not knowing whether f
awaited g
). If f
is not interested in the result of g
it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f
might have to trigger several async
calls sequentially await
ing several g_1
, g_2
,... to resolve, then you can build a test chain by asserting in the stub of g_n+1
that the dummy-promise of g_n
has been resolved. In general your approach to test a dummy-promise for its status is fine.
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned byf
to expose the value with whichg
resolves. Your example withthen
syntax is exactly how one might do this withoutasync/await
.
– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return whatg
returns. You might want to call severalg_1
,g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.
– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to whatg
actually does, and what of that operation is acceptable to expose throughf
. Certainly the test would be cleaner without having to track the completion status ofg
manually, as I do in my answer.
– sripberger
Dec 3 '18 at 17:19
add a comment |
Your example for f
shows flawed code design which becomes more obvious if you write the same function without async/await
syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g
resolved becomes hard to tell (assuming you don't know if f
returned g
's promise, which is the same as not knowing whether f
awaited g
). If f
is not interested in the result of g
it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f
might have to trigger several async
calls sequentially await
ing several g_1
, g_2
,... to resolve, then you can build a test chain by asserting in the stub of g_n+1
that the dummy-promise of g_n
has been resolved. In general your approach to test a dummy-promise for its status is fine.
Your example for f
shows flawed code design which becomes more obvious if you write the same function without async/await
syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g
resolved becomes hard to tell (assuming you don't know if f
returned g
's promise, which is the same as not knowing whether f
awaited g
). If f
is not interested in the result of g
it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f
might have to trigger several async
calls sequentially await
ing several g_1
, g_2
,... to resolve, then you can build a test chain by asserting in the stub of g_n+1
that the dummy-promise of g_n
has been resolved. In general your approach to test a dummy-promise for its status is fine.
answered Nov 28 '18 at 23:52
B MB M
1,53211731
1,53211731
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned byf
to expose the value with whichg
resolves. Your example withthen
syntax is exactly how one might do this withoutasync/await
.
– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return whatg
returns. You might want to call severalg_1
,g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.
– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to whatg
actually does, and what of that operation is acceptable to expose throughf
. Certainly the test would be cleaner without having to track the completion status ofg
manually, as I do in my answer.
– sripberger
Dec 3 '18 at 17:19
add a comment |
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned byf
to expose the value with whichg
resolves. Your example withthen
syntax is exactly how one might do this withoutasync/await
.
– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return whatg
returns. You might want to call severalg_1
,g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.
– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to whatg
actually does, and what of that operation is acceptable to expose throughf
. Certainly the test would be cleaner without having to track the completion status ofg
manually, as I do in my answer.
– sripberger
Dec 3 '18 at 17:19
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned by
f
to expose the value with which g
resolves. Your example with then
syntax is exactly how one might do this without async/await
.– sripberger
Dec 3 '18 at 16:08
I don't necessarily agree that this is flawed code design. It could be that he doesn't want the promise returned by
f
to expose the value with which g
resolves. Your example with then
syntax is exactly how one might do this without async/await
.– sripberger
Dec 3 '18 at 16:08
I see your point, and yes there might be cases when you don't want to return what
g
returns. You might want to call several g_1
, g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.– B M
Dec 3 '18 at 16:51
I see your point, and yes there might be cases when you don't want to return what
g
returns. You might want to call several g_1
, g_2
,... as I mentioned, or just one but might want to prevent access to a reference - however even in those cases I still think it's cleaner to return some specific mapping, an instance ID for example. Completly hiding the result of a proxied function while wanting testable guarantees that the proxied function ran seems a bit paradoxical.– B M
Dec 3 '18 at 16:51
That's reasonable. Really it all comes down to what
g
actually does, and what of that operation is acceptable to expose through f
. Certainly the test would be cleaner without having to track the completion status of g
manually, as I do in my answer.– sripberger
Dec 3 '18 at 17:19
That's reasonable. Really it all comes down to what
g
actually does, and what of that operation is acceptable to expose through f
. Certainly the test would be cleaner without having to track the completion status of g
manually, as I do in my answer.– sripberger
Dec 3 '18 at 17:19
add a comment |
Instead of stubbing then
, you're best off stubbing g
in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f
to make sure f
waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f
wait on any promise that waits at least as long as it takes for g
to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
add a comment |
Instead of stubbing then
, you're best off stubbing g
in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f
to make sure f
waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f
wait on any promise that waits at least as long as it takes for g
to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
add a comment |
Instead of stubbing then
, you're best off stubbing g
in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f
to make sure f
waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f
wait on any promise that waits at least as long as it takes for g
to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
Instead of stubbing then
, you're best off stubbing g
in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f
to make sure f
waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f
wait on any promise that waits at least as long as it takes for g
to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
edited Dec 3 '18 at 17:53
answered Dec 3 '18 at 16:03
sripbergersripberger
1,070116
1,070116
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53529435%2fhow-can-i-assert-a-function-has-awaited-an-async-function-using-should-js%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Does
should-sinon
pull insinon
? Presumably you'd want torequire('sinon')
beforerequire('should-sinon')
?– James
Nov 28 '18 at 23:32