-
Notifications
You must be signed in to change notification settings - Fork 1.4k
RFC: Mocked Timer APIs #1827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Could you elaborate on cleanup and how it relates to concurrent tests? We have some open issues to prevent exiting while the event loop is busy, and to enable us to fail a test down the line even if all assertions pass, e.g. due to a leaking timer. |
I hadn't fully considered the concurrency problem because building this in requires you to override globals which could be used by any concurrent test. I'm not sure it's even possible to (fully) detect what timer was created by what test. The closest thing I've got is looking at the stack trace, and that has all sorts of problems. The other option here is always mocking timers. Which would be good for performance, but (based on Jest's early state) really bad for developer experience. Maybe this is the sort of thing opted into on a file basis: const test = require('ava');
const c = test.clock(); // must be called immediately?
test('timers', async t => {
let called = 0;
setTimeout(() => called++, 1000);
await c.time(1000); // move timers ahead in milliseconds
t.is(called, 1);
}); |
I wonder if there's an extension of #1456 that might be useful here. Just sketching: // clock.js
import test from 'ava'
const withClock = test.serial.use(async (t, next) => {
const {destroy, ...fns} = createClock()
try {
await next({...t, ...fns})
} finally {
destroy()
}
})
export default withClock import test from './clock'
test('timers', async t => {
let called = 0;
setTimeout(() => called++, 1000);
await t.time(1000); // move timers ahead in milliseconds
t.is(called, 1);
}); That is to say, perhaps we can create new test interfaces that have specific behavior, and that can decorate the execution context. They could force synchronous execution, etc. There's a bit of #222 in here, in that it'd make sense to allow |
Hmm... I would almost prefer a way of saying that "this test needs to be run serially" as part of creating the clock. Otherwise you'd almost certainly end up with multiple const test = require('ava');
const testWithClock = require('../../test-helpers/ava-with-clock'); vs test('timers', async t => {
let c = await t.clock();
// Error: Cannot call t.clock() in tests that run concurrently (use `test.serial`)
}); Another idea: If we could hook into the test scheduler, you could make const test = require('ava');
test('timers', async t => {
let c = await t.clock();
let called = 0;
setTimeout(() => called++, 1000);
await c.time(1000); // move timers ahead in milliseconds
t.is(called, 1);
}); Since some test files can end up being hundreds of tests (where concurrency has a huge impact), it would be good if you didn't need to make every test in the file run serially. |
Oh I like that! Perhaps Then a decorator could implement |
If my code uses |
Nvm, got it:
|
With #2435 I'm exploring ways to create custom |
Writing this up to go along with #1825, because it's another major feature that I think Ava could include and improve upon by baking it into the framework.
Why add timer mocking?
Timer mocking is important for:
Why not a separate library?
Examples
API type definition
How to implement?
SinonJS has already done the work of splitting out their timer faking to a separate package:
https://github.com/sinonjs/lolex
It's already usable within Ava, but I think that we could improve the experience further.
Why
await c.time/tick/frame()
?Right now it's impossible to completely mock out promises because of
async-await
. The fix is to wait a microtask without actually clearing it.This does mean that we aren't completely mocking away timers, but it might actually be a good thing in case code does depend on microtasks not being run immediately.
The text was updated successfully, but these errors were encountered: