This is the test:
describe('blinkyDancer', function() {
const { expect } = chai;
let blinkyDancer;
let clock;
let timeBetweenSteps = 100;
beforeEach(function() {
clock = sinon.useFakeTimers();
blinkyDancer = makeBlinkyDancer(10, 20, timeBetweenSteps);
});
describe('dance', function() {
it('should call step at least once per second', function() {
sinon.spy(blinkyDancer, 'step');
expect(blinkyDancer.step.callCount).to.be.equal(0);
// Depending on your solution, you may need an extra tick
// If it does, try and refactor your solution to not
// clock.tick(timeBetweenSteps);
clock.tick(timeBetweenSteps);
expect(blinkyDancer.step.callCount).to.be.equal(1);
clock.tick(timeBetweenSteps);
expect(blinkyDancer.step.callCount).to.be.equal(2);
});
});
});
This is the code being tested:
const makeDancer = function(top, left, timeBetweenSteps) {
const dancer = {};
dancer.$node = $('<span class="dancer"></span>');
dancer.step = function() {
// This version passes with only one initial tick in
// the code which is the expected outcome
setTimeout(() => dancer.step(), timeBetweenSteps);
// This version fails with only one initial tick in
// the code but passes if given two initial ticks
// setTimeout(dancer.step, timeBetweenSteps);
};
dancer.setPosition = function(top, left) {
const styleSettings = {
top: top,
left: left
};
dancer.$node.css(styleSettings);
};
dancer.setPosition(top, left);
dancer.step();
return dancer;
};
const makeBlinkyDancer = function(top, left, timeBetweenSteps) {
const blinkyDancer = makeDancer(top, left, timeBetweenSteps);
const oldStep = blinkyDancer.step;
blinkyDancer.step = function() {
oldStep();
blinkyDancer.$node.toggle();
};
return blinkyDancer;
};
The problem:
If the SetTimeout callback is just the step function, setTimeout(dancer.step, timeBetweenSteps), the test fails unless an extra tick of clock.tick(timeBetweenSteps) is given. On the other hand, if you wrap the step function and pass the wrapper to the callback, setTimeout(() => dancer.step(), timeBetweenSteps), then the test passes just fine with only 1 initial tick.
My question:
Why does having a wrapped function make the test pass? I have already figured out multiple ways to make the test pass that I can sort of imagine an explanation for why they pass, but I can't come up with anything great for this one. The only leads I have are that Sinon replaces the setTimeout function with a synchronous version in testing environments, so maybe that messed with the functionality.