It's a testing framework that is used to test javascript code. It's a complete and easy to set-up testing solution.
npm install --save-dev jest
-
To write your tests you need to create your test files next to the components files, and to configure it to search into only the folder that contains the files (e.g.
app/modules/vue-components
) for.spec.js
and.test.js
, This restriction is important to avoid searching in thenode_modules
folder and other folders that contain libraries and dependencies. -
Jest configurations:
-
All jest configurations are locates inside
package.json
file under keyjest
, and this is an example of the configurations:"jest": { "moduleFileExtensions": [ "js", "vue" ], "testMatch": [ "**/**/modules/vue-components/**/*.(spec|test).js" // search for test files in this folder ], "transform": { ".*\\.(vue)$": "vue-jest", "^.+\\.js$": "<rootDir>/node_modules/babel-jest" }, "transformIgnorePatterns": [ "/node_modules/" ], "collectCoverage": true, "coverageDirectory": "./vue-test-coverage", "collectCoverageFrom": [ "**/**/modules/vue-components/**/*.vue", "!**/node_modules/**" ], "coverageReporters": [ "html", "text", "text-summary", "lcov" ] }
-
EsLint will throw errors when using test global-object in the node environment, so:
- add
jest: true
to the env in .eslintrc.js file
-
Use the
describe
keyword followed by a short description of what the suite is testing and one or more specs. -
A best practice is to start a sentence with
“it”
and then complete the sentence with the description of what the suite is testing.describe('suite description', () => { it('describes the spec', () => { const myVar = true; expect(myVar).toBe(true); }); });
Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this:
- Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will fail.
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
- To write an async test, use the async keyword in front of the function passed to test.
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
-
If you don't use promises, you can use callbacks. For example, let's say that fetchData, instead of returning a promise, expects a callback, i.e. fetches some data and calls callback(null, data) when it is complete. You want to test that this returned data is the string 'peanut butter'.
-
By default,Jest tests complete once they reach the end of their execution. That means this test will not work as intended with Async code
- The problem is that the test will complete as soon as fetchData completes, before ever calling the callback.
-
There is an alternate form of test that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called (done). -> Jest will wait until the done callback is called before finishing the test.
// ---------------------------Don't do this!--------------------------- //
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
// ------------------------------Do this!------------------------------ //
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
expect.assertions(number)
- It verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called.
test('doAsync calls both callbacks', () => {
expect.assertions(2); // ensures that both callbacks actually get called
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
Use test.concurrent
or describe.concurrent
if you want the test to run concurrently (parallel).
enzyme: it allows us to render components
It's an useful tool whenever you want to make sure your UI does not change unexpectedly.
- in react to see if component changed without rendering it each time
-
Testing reducers
: It's straight forward as reducer-functions are pure functions -
Testing actions
:
Tests must not depend on external services. Use mocking tools such as jest.mock to avoid it.
-
Jest mock function
jest.fn()
- it plays the role of a real function.
- we use it as a replacement for a real/complex function that we don't want to involve in our tests to keep it lightweight and simple
-
Note: when using typescript it will raise a warning that the mocked function doesn't have methods of the jest mocked function like
mockReturnValue()
, so we will have to use type-casting:import { useStore } from 'vuex'; // mock out libraries to mock functions from them jest.mock('vuex'); const useStoreMock = useStore as jest.Mock; // type casting so that we can access the "mockResolvedValue" method it('retrieves filtered jobs from store', () => { useStoreMock.mockReturnValue({ getters: { FILTERED_JOBS: [{ id: 1 }] } }); const result = useFilteredJobs(); expect(result.value).toEqual([{ id: 1 }]); });
They are mock functions like jest.fn()
but with focus on javascript timer functions
-
jest.useFakeTimers
- method that auto-mocks all the javascript timer functions, ex:
setTimeout
,setInterval
- this help us to not have to mock every timer function we use as it will automatically do that
- method that auto-mocks all the javascript timer functions, ex:
-
jest.useRealTimers
- method that returns the mocked functions to their original native javascript implementations
- to undo using the fake timers from jest and returning to the native js-timers
-
Note:
- if
jest.useFakeTimers
doesn't work, you can usejest.spyOn(global, "setInterval")
- comment from issue in Github:
they are mocked, but they are not mock functions. This is on purpose. See #5171 (although that's not the PR that landed, it contains notes of all the differences between "modern" and "legacy" fake timers)
- if
We don't want out unit-test component to make an actual API-request. It's slow, complex and prone to error. Rather, we can instruct Jest to mock out a dependency (like axios
) and Jest will replace all of the axios
object methods with mock functions.
if you had importing problems, add axios to the transformIgnorePatterns in configurations
// import axios
// Must be on the same scope as your `import` to be hoisted to the top of the file not the scope
jest.mock('axios'); // mock the axios object and all its methods as jest functions
// (implicit): telling it to make it use this function when it's called
jest.mock('axios', () => ({
get: () => Promise.resolve({ data: [{ val: 1 }] })
}));
// or
// (explicit): to simulate what axios return from GET-REQUEST
axios.get.mockResolvedValue({ data: [{ val: 1 }] }); // control the resolved value from the mocked method (get())
-
mockReset()
method:-
it's a clean-up method that clear any custom mock-implementation set-up in any spec
-
it's suitable inside a
afterEach
blockafterEach(() => { axios.get.mockReset(); // clearing any custom mock-implementation set-up in any spec });
-