问题
I've a web component for auto-logout functionality which shows modal window with a message on 59th minute and stay for another minute in case of no activity. And logs out the user if user doesn't click anywhere on the window. So, no activity for an hour will logout the user automatically. This works fine.
Now, to test this functionality, I tried to use sinonjs
. I used FakeTimers
but couldn't able to achieve the result. I am trying to test that modal window with message shows up.
Here's the code:
const { When } = require('cucumber'); // eslint-disable-line import/no-extraneous-dependencies
const fs = require('fs');
const path = require('path');
let clock;
async function setupSinon() {
const sinonPath = require.resolve('sinon');
const content = await new Promise((resolve, reject) => {
fs.readFile(
path.join(sinonPath, '../../pkg/sinon.js'),
'utf-8',
async (error, cont) => {
if (error) return reject(error);
resolve(cont);
},
);
});
// creating <script> element for sinonjs to execute on the page
await browser.execute((content) => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.text = content;
document.head.appendChild(script);
}, content);
}
async function iWaitForNMinutes() {
await setupSinon();
await browser.execute(() => {
before(() => {
clock = sinon.useFakeTimers();
});
clock = sinon.useFakeTimers({
now: Date.now(),
shouldAdvanceTime: true,
toFake: ['setTimeout'],
});
clock.tick('59:00'); // advancing the clock to 59 minutes so that auto-logout modal window popup, but this doesn't work
after(() => {
clock.restore();
});
setTimeout(() => {}, 60000);
});
}
When(/^I wait for minutes$/, iWaitForNMinutes);
module.exports = {
iWaitForNMinutes,
};
sinon 5.0.10
How to user sinonjs
FakeTimer to advance the time to n minutes and then wait actually for n minutes ?
回答1:
Sinon's fake timers are pretty easy to work with and are my favourite feature of sinon.
The usage goes like this
In your code
// in your code
eatCake() {
setTimeout(function() {
// eat cake after 10s
}, 10000); // 10000 === 10s
}
In your test
clock = sinon.useFakeTimers();
clock.tick(9000);
// check if cake is eaten - answer will be false
clock.tick(1000); // 9000 + 1000 === 10s
// check if cake is eaten - answer will be true
So sinon basically fast forwards (programatically) the timer so our test can check for the desired result without actually waiting the time because all test frameworks usually have a wait timeout of 2s after which a test case will fail.
In your case, to wait for 59 minutes, you could write
clock.tick(1000 * 60 * 59); // 59 minutes
// check if the modal has opened up
clock.tick(1000 * 60 * 1); // 1 minute
// check if the user is logged out
And don't forget to restore the clock at the end as you've already done.
clock.restore();
回答2:
Your general problem is probably in the await
before your function (browser.execute
):
- advancing the clock before await is pointless (regarding anything like setTimeOut inside)... since any of those inner timing functions is not yet set up.
- advancing the clock after the
await <function>
is pointless because indeed, theawait
will wait...
The solution is to not use await with your (async) function but treat it like any other promise-returning function (which any async function actually is).
it('some test', async () => {
await new Promise((resolve, reject) => {
// normally used with await
functionToBeTested(...some params).then(
(value) => {
log('now it happend')
resolve()
},
(reason) => {
reject(reason)
}
)
// by not using await (despite deleteAction being an sync function)
// but rather just chaining a then, this clock.tick gets run while
// deleteAction is "running" resp. waiting for some timeout:
clock.tick(1000)
})
... some more asserts on the result...
(we can be rest-assured the command under test is completed here)
})
来源:https://stackoverflow.com/questions/50646921/sinonjs-advance-clock-to-59-minutes-and-wait-for-1-minute-actually