Reputation: 7092
I want to create a unit test for an asynchronous process. I have created a bare-bones mock up of the situation, using a timeout to delay an alert. (The real process will be the loading of a JS file on the fly).
I'm just beginning with Mocha, Chai and Sinon. I've created a folder called vendor
alongside my HTML file. This contains the latest versions of mocha.css, mocha.js, chai.js and sinon.js.
The code below works fine if I comment out the setTimeout()
call. How should I alter it so that the sinon.assert...
calls will wait for the delayed action to occur?
<!DOCTYPE html>
<head>
<title>Delayed alert test</title>
</head>
<body>
<div id="mocha"><p><a href=".">Index</a></p></div>
<div id="messages"></div>
<div id="fixtures"></div>
<link rel="stylesheet" type="text/css" href="vendor/mocha.css" />
<script src="vendor/mocha.js"></script>
<script src="vendor/chai.js"></script>
<script src="vendor/sinon.js"></script>
<script>
mocha.setup('bdd')
var spy = sinon.spy(window, 'alert')
describe("Test", function() {
describe("#alert", function() {
it("should show an alert", function(done) {
this.timeout(5000)
setTimeout(function () { // Works if these 2 lines...
alert ("Once")
alert ("Twice")
}, 2000) // are commented out
sinon.assert.called(spy)
sinon.assert.calledTwice(spy)
sinon.assert.calledWithExactly(spy, "Once")
sinon.assert.calledWithExactly(spy, "Twice")
done()
});
});
})
mocha.run();
</script>
</body>
</html>
Upvotes: 2
Views: 6622
Reputation: 74680
Your assertions and done()
(if the test ever got there) are being called as soon as the timeout is setup.
The test sets a timeout
this.timeout(5000)
Schedules your test to run in 2 seconds, and moves on immediately.
setTimeout(...
Checks an assertion that fails
sinon.assert.called(spy)
Then exits before the setTimeout
has a chance to run.
The assertions need to run after the setTimeout has completed and as we are in a browser, the asynchronous assertions need to be captured in a try/catch block so the actual exception can be passed back to mocha via done()
.
Most asynchronous API's allow you to pass a "callback" function in, which is called after the asynchronous tasks completion which is normally the function you put your assertions/done in.
In your case it's a little more literal...
describe("Test", function() {
describe("#alert", function() {
it("should show an alert", function(done) {
this.timeout(5000)
var stub = sinon.stub(window, 'alert')
var assertion = function(){
try {
sinon.assert.called(stub)
sinon.assert.calledTwice(stub)
sinon.assert.calledWithExactly(stub, "Oce")
sinon.assert.calledWithExactly(stub, "Twice")
done()
} catch ( e ) {
done( e )
}
}
setTimeout(function () {
alert ("Once")
alert ("Twice")
assertion()
}, 2000)
});
});
})
https://jsfiddle.net/6w4p7rxz/
Note, I changed the spy
to stub
for less clicking. Also use chai! Maybe sinon-chai as well to make things easier to read.
Upvotes: 2