-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Proposal: await jasmine.clock().asyncTick() #1725
Comments
This sounds reasonable and I'd be happy to review a Pull Request to add an awaitable Sorry for the delay. Thanks for using Jasmine! |
I actually have some time now, so I'm taking a look. Seems like I should inject DelayedFunctionScheduler with (customPromise || global.Promise) ? |
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Testing with mock clocks can often turn into a real struggle when dealing with situations where some work in the test is truly async and other work is captured by the mock clock. This can happen for many reasons, but as one example: An asynchonrous change from a task in the mocked clock may change DOM where a resize observer then gets triggered. This browser API is truly asynchronous and would require the user to wait real time for it to fire. If there is follow-up work after the resize observer fires, it may be captured by the mock clock again. This would require the tester to write something like the following: ``` // flush the timer jasmine.clock().tick(); // wait for resize observer await new Promise(resolve => setTimeout(resolve)); // flush follow-up work from the resize observer callback jasmine.clock().tick(); ``` When using mock clocks, testers are always forced to write tests with intimate knowledge of when the mock clock needs to be ticked. Oftentimes, the purpose of using a mock clock is to speed up the execution time of the test when there are timeouts involved. It is not often a goal to test the exact timeout values. This can cause tests to be riddled with `tick`. It ideal for test code to be written in a way that is independent of whether a mock clock is installed. For example: ``` document.getElementById('submit'); // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1)) ``` When mock clocks are involved, the above may not be possible if there is some delay involved between the click and the request to the API. Instead, developers would need to manually tick the clock beyond the delay to trigger the API call. This commit attempts to resolve these issues by adding a feature to the clock which allows it to advance on its own with the passage of time, just as clocks do without mocks installed. It also allows for some breathing time so any unmocked micro and macrotasks are given space to execute as well. This feature would also address both jasmine#1725 and jasmine#1932. `asyncTick` can be accomplished by enabling the auto tick feature and then waiting for a promise with a timout to be resolved (`await new Promise(resolve => setTimeout(resolve, 20))`) where `setTimeout` is captured by the mock clock and flushed while the code is waiting for the promise to resolve. resolves jasmine#1725 resolves jasmine#1932 All credit goes to @stephenfarrar for this.
Several previous issues have discussed the way mock clock doesn't work very well with Promises #1659 #1282 #710 . Here I have a proposal for a new method on the mock clock that will inter-operate with Promises nicely.
It's quite common to write code like this:
You might expect
jasmine.clock().tick(2000)
to log both A and B, but it will log neither A nor B! The issue is that.tick()
runs any scheduled timeouts one after the other, synchronously, and doesn't allow microtasks to run.Instead, we need to run a timeout, wait for microtasks to run, and then run the remaining timeouts, repeat. This is pretty close to the way the JS engine actually works.
I propose adding an
.asyncTick()
method that does what I've just described. The test would call it like this:One difficulty is knowing how to wait for microtasks to run. In previous issues, people pointed out that various Promise implementations work in different ways. My solution to this (in other settings) has been to schedule a (real)
setTimeout
. This does slow the test down, but it's a lot better than using a real clock.Another alternative could be to call jasmine's
clearStack()
. If a user has a Promise implementation that is slower than whateverclearStack()
does, we could just not care. (They can always get a better Promise implementation!) This would be my preferred approach.If this seems like a reasonable proposal, I will try to code it up and send a PR. Seems like I need to add a
runScheduledFunctionsAsync
method toDelayedFunctionScheduler
.The text was updated successfully, but these errors were encountered: