Author: Muhammad AbdulMoiz
easy to configure
meaningful error messages
parallel tests
built in mocking
async tests
smart watchmode
compile to js friendly
familiar syntax
Installing Jest
// Install Jest using npm
npm install --save-dev jest
// Installing via yarn
yarn add --dev jest
package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
}
}
Default test files consideration
__tests__
*.spec.js
*.js
package.json
*.test.js
*.spec.js
Over ride default regex via package.json
{
"jest": {
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$"
}
}
Jest uses jasmine as a test runner but it does so much around it
/*Global Methods*/
- afterAll(fn)
- afterEach(fn)
- beforeAll(fn)
- beforeEach(fn)
- describe(name, fn)
- describe.only(name, fn)
- describe.skip(name, fn)
- require.requireActual(moduleName)
- require.requireMock(moduleName)
- test(name, fn)
- test.only(name, fn)
- test.skip(name, fn)
describe('My Test suite', () => {
beforeAll(fn);
beforeEach(fn);
test('first test', fn);
test('second test', fn);
.
.
.
afterEach(fn);
afterAll(fn)
});
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
test('object assignment', () => {
const data = {one: 1};
data['two'] = 2;
expect(data).toEqual({one: 1, two: 2});
});
test('adding positive numbers is not zero', () => {
for (let a = 1; a < 10; a++) {
for (let b = 1; b < 10; b++) {
expect(a + b).not.toBe(0);
}
}
});
When you're writing tests, you often need to check that values meet certain conditions. expect gives you access to a number of "matchers" that let you validate different things.
expect.anything()
expect.any(constructor)
expect.arrayContaining(array)
expect.objectContaining(object)
expect.stringContaining(string)
expect.stringMatching(regexp)
.not
.resolves
.rejects
.toBe(value)
.toHaveBeenCalled()
You can use expect.extend to add your own matchers to Jest.
expect.extend({
toBeDivisibleBy(received, argument) {
const pass = (received % argument == 0);
if (pass) {
return {
message: () => (
`expected ${received} not to be divisible by ${argument}`
),
pass: true,
};
} else {
return {
message: () => (`expected ${received} to be divisible by ${argument}`),
pass: false,
};
}
},
});
test('even and odd numbers', () => {
expect(100).toBeDivisibleBy(2);
expect(101).not.toBeDivisibleBy(2);
});
test('async test', (done) => {
someFn(function(err){
if(err){
return done.fail('Error message!');
}
expect()
done();
})
});
test('async test', (done) => {
return somePromise()
.then(() => {
//testing promise resolve
expect();
})
.catch(() => {
// testing rejections
expect();
})
})
While running asynchronous test either you need to return a promise or use jasmine done argument
//Wrong way of testing asynchronous method
//It won't fail OR run into any error because expect is never run
test('async test', () => {
someFn(function(err){
expect();
})
});
If you are not using done for testing in callbacks, its not testing correctly
//Wrong way of testing asynchronous method
//It won't fail OR run into any error because expect is never run
test('async test', () => {
someFn(function(err){
expect();
})
});
If you are not using done for testing in callbacks, its not testing correctly
Running suites in parallel will definitely increase speed which decreases running time
Mock functions make it easy to test the links between code by erasing the actual implementation of a function
const someMockFn = jest.fn();
// The function was called exactly once
expect(someMockFunction.mock.calls.length).toBe(1);
// The first arg of the first call to the function was 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg');
// The second arg of the first call to the function was 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
// This function was instantiated exactly twice
expect(someMockFunction.mock.instances.length).toBe(2);
// The object returned by the first instantiation of this function
// had a `name` property whose value was set to 'test'
expect(someMockFunction.mock.instances[0].name).toEqual('test');
const myMock = jest.fn();
console.log(myMock());
// > undefined
myMock.mockReturnValueOnce(10)
.mockReturnValueOnce('x')
.mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock());
// > 10, 'x', true, true
const myMockFn = jest.fn(cb => cb(null, true));
myMockFn((err, val) => console.log(val));
// > true
myMockFn((err, val) => console.log(val));
// > true
// foo.js
module.exports = function() {
// some implementation;
};
// test.js
jest.mock('../foo'); // this happens automatically with automocking
const foo = require('../foo');
// foo is a mock function
foo.mockImplementation(() => 42);
foo();
// > 42
Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.
Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your DOM tree
As we haven't look into the DOM testing with test, consider following example
expect(someFn()).toMatchSnapshot();// It will automatically create snapshot files
//In "ROOT/__snapshots__" folder
//Snap shot file contain following data w.r.t each test
exports[`should test ....`] = `<some output value>`;
//If test is failed due to old snap shot issue we need to update snapshots
//command for updating snapshots
jest --updateSnapshot
//Installing required dependencies
npm install --save-dev ts-jest @types/jest
//Modify packageJSON
{
"jest": {
"transform": {
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json",
"jsx"
]
}
}
Jest is built by facebook primarily for testing React framework but JEST team has done a great job and it can be use with any Javascript framework
Following is an article for discussion of Angular 2 testing with JEST
//Installing jest-codemods globally
npm install -g jest-codemods
//At root of your app run following command
jest-codemods
If you are using Mocha, AVA, chai or Tape, you can use the third-party jest-codemods to do most of the dirty migration work. It runs a code transformation on your codebase using jscodeshift.
It will ask for the frame work to migrate and run migration scripts as shown below
Thanks for watching and listening!!
Is there any questions?