Provide alternatives to using setTimeout in frontend tests
This commit is contained in:
parent
9d4450263f
commit
a0c4645257
1 changed files with 182 additions and 1 deletions
|
@ -187,6 +187,7 @@ export default function doSomething() {
|
|||
visitUrl('/foo/bar');
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// my_module_spec.js
|
||||
import doSomething from '~/my_module';
|
||||
|
@ -213,7 +214,187 @@ Further documentation on the babel rewire pluign API can be found on
|
|||
|
||||
#### Waiting in tests
|
||||
|
||||
If you cannot avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) in tests, please use the [Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
|
||||
Sometimes a test needs to wait for something to happen in the application before it continues.
|
||||
Avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
|
||||
because it makes the reason for waiting unclear and if passed a time larger than zero it will slow down our test suite.
|
||||
Instead use one of the following approaches.
|
||||
|
||||
##### Promises and Ajax calls
|
||||
|
||||
Register handler functions to wait for the `Promise` to be resolved.
|
||||
|
||||
```javascript
|
||||
const askTheServer = () => {
|
||||
return axios
|
||||
.get('/endpoint')
|
||||
.then(response => {
|
||||
// do something
|
||||
})
|
||||
.catch(error => {
|
||||
// do something else
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
**in Jest:**
|
||||
|
||||
```javascript
|
||||
it('waits for an Ajax call', () => {
|
||||
return askTheServer().then(() => {
|
||||
expect(something).toBe('done');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('waits for an Ajax call', done => {
|
||||
askTheServer()
|
||||
.then(() => {
|
||||
expect(something).toBe('done');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
```
|
||||
|
||||
If you are not able to register handlers to the `Promise`—for example because it is executed in a synchronous Vue life
|
||||
cycle hook—you can flush all pending `Promise`s:
|
||||
|
||||
**in Jest:**
|
||||
|
||||
```javascript
|
||||
it('waits for an Ajax call', () => {
|
||||
synchronousFunction();
|
||||
jest.runAllTicks();
|
||||
|
||||
expect(something).toBe('done');
|
||||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
You are out of luck. The following only works sometimes and may lead to flaky failures:
|
||||
|
||||
```javascript
|
||||
it('waits for an Ajax call', done => {
|
||||
synchronousFunction();
|
||||
|
||||
// create a new Promise and hope that it resolves after the rest
|
||||
Promise.resolve()
|
||||
.then(() => {
|
||||
expect(something).toBe('done');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
```
|
||||
|
||||
##### Vue rendering
|
||||
|
||||
To wait until a Vue component is re-rendered, use either of the equivalent
|
||||
[`Vue.nextTick()`](https://vuejs.org/v2/api/#Vue-nextTick) or `vm.$nextTick()`.
|
||||
|
||||
**in Jest:**
|
||||
|
||||
```javascript
|
||||
it('renders something', () => {
|
||||
wrapper.setProps({ value: 'new value' });
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.text()).toBe('new value');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('renders something', done => {
|
||||
wrapper.setProps({ value: 'new value' });
|
||||
|
||||
wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(wrapper.text()).toBe('new value');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
```
|
||||
|
||||
##### `setTimeout()` / `setInterval()` in application
|
||||
|
||||
If the application itself is waiting for some time, mock await the waiting. In Jest this is already
|
||||
[done by default](https://gitlab.com/gitlab-org/gitlab-ce/blob/a2128edfee799e49a8732bfa235e2c5e14949c68/jest.config.js#L47)
|
||||
(see also [Jest Timer Mocks](https://jestjs.io/docs/en/timer-mocks)). In Karma you can use the
|
||||
[Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
|
||||
|
||||
```javascript
|
||||
const doSomethingLater = () => {
|
||||
setTimeout(() => {
|
||||
// do something
|
||||
}, 4000);
|
||||
};
|
||||
```
|
||||
|
||||
**in Jest:**
|
||||
|
||||
```javascript
|
||||
it('does something', () => {
|
||||
doSomethingLater();
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(something).toBe('done');
|
||||
});
|
||||
```
|
||||
|
||||
**in Karma:**
|
||||
|
||||
```javascript
|
||||
it('does something', () => {
|
||||
jasmine.clock().install();
|
||||
|
||||
doSomethingLater();
|
||||
jasmine.clock().tick(4000);
|
||||
|
||||
expect(something).toBe('done');
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
```
|
||||
|
||||
##### Events
|
||||
|
||||
If the application triggers an event that you need to wait for in your test, register an event handler which contains
|
||||
the assertions:
|
||||
|
||||
```javascript
|
||||
it('waits for an event', done => {
|
||||
eventHub.$once('someEvent', eventHandler);
|
||||
|
||||
someFunction();
|
||||
|
||||
function eventHandler() {
|
||||
expect(something).toBe('done');
|
||||
done();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In Jest you can also use a `Promise` for this:
|
||||
|
||||
```javascript
|
||||
it('waits for an event', () => {
|
||||
const eventTriggered = new Promise(resolve => eventHub.$once('someEvent', resolve));
|
||||
|
||||
someFunction();
|
||||
|
||||
return eventTriggered.then(() => {
|
||||
expect(something).toBe('done');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### Migrating flaky Karma tests to Jest
|
||||
|
||||
|
|
Loading…
Reference in a new issue