Jest
Introduction
This is a page to introduce Jest. Facebook employs a lot of the people who develop jest and can be and is used for test in other frameworks.
Choices
Popular tools for testing are Enzyme and Jasmine/Mocha. Jest is built on top of Jasmine/Mocha. Jest works with or without React and has a great assertion library.
Why Not Enzyme
This has over 200+ issues open and 26 of these are bugs. Jest is recommended by the react team.
Jest vs Mocha
Jest and Mocha both run tests sync and async but jest also has the following features.
- Spies (stubbing methods to spy)
- Snapshot testing
- Module mocking (stubbing objects)
Getting Started
Naming Test
__tests__/*.js
*.spec.js
*.test.js
Example Test in Jest
Describe is the suite, it is the test
describe("The question list ", ()=> { it ("should display a list of items", ()=> { expect(2+2).toEqual(4); })
it ("should display a list of items", ()=> { expect(2+4).toEqual(6); })
})
Setup and Teardown
This can be done using
BeforeEach BeforeAll
beforeAll(()=>{ console.log("Hello"); });
and
AfterEach AfterAll
afterAll(()=>{ console.log("Hello"); });
Note does not matter what order you write them in.
Skipping and Isolating Tests
Add keyword only to include tests
it.only ("should display a list of items", ()=> { expect(2+2).toEqual(4); })
Add keyword skip to exlude tests
it.skip ("should display a list of items", ()=> { expect(2+2).toEqual(4); })
Async Tests
it('async test 1', done=> { setTimeout(done, 100) });
it('async test 2',()=> { return new Promise( resolve => setTimeout(resolve,100) ); });
it('async test 1', async ()=> await delay(100) );
Mocking
Create directory __mocking__ at the same level as node_modules
Here is an example for mocking fetch package isomorphic-fetch
let __value = 42;
const isomorphicFetch = jest.fn( ()=> __value);
isomorphicFetch.__setValue = v => __value = v;
export default isomorphicFetch;
And the test case. We import the function to be mocked from out __mocks__/isomorphic-fetch.js
import { handleFetchQuestion } from './fetch-question-saga';
import fetch from 'isomorphic-fetch';
describe("Fetch question s saga", ()=>{
beforeAll( () => {
fetch.__setValue([{question_id:42}] );
});
it ("should fetch the question saga", async ()=> {
const gen = handleFetchQuestion({question_id:42});
const { value } = await gen.next();
expect(value).toEqual( [{question_id:42}] )
expect(fetch).toHaveBeenCalledWith('/api/questions/42');
});
});
Redux and Testing mapStateToProps
You can easily test the map from state to props. e.g.
import { mapStateToProps } from '../QuestionDetail';
it("should map the state to props correctly", () => {
// Sample App State with sampleQuestion being object in state
const sampleQuestion {
question_id:42,
body: "Space is big"
};
const appState = {
questions: [sampleQuestion]
};
// Sample Props
const ownProps = {
question:42
});
const componentState = mapStateToProps(appState, ownProps)
expect(componentState).toEqual(sampleQuestion);
}
Sample test with state
Bit of a lot of code but here goes
Create component to test
import React from 'react';
export default class extends React.Component {
constructor)(...args) {
super(...args);
this.state = {
count: -1
}
}
async comonentDidMount () {
let { count } = await NotificationService.GetNotification();
this.setState( {
count
});
}
render() {
return (
<section className="mt-3 mb-2">
<div className="notifications">
{this.state.count != -1 ? '${this.state.count} Notifications Awaiting!' : 'Loading...'}
</div>
</section>
)
}
}
Create a Service
export default {
async GetNoitifications() {
console.warning("REAL NOTIFICATION SERVICE!");
await delay(1000);
return { count: 42 };
}
}
Test Case
import NotificationsViewer from '../NotificationsViewer';
import renderer from 'react-test-renderer';
import React from 'react';
import delay from 'redux-saga';
// NOTE Local module you need to pass in url of notification service
jest.mock('../../services/NotificationService');
// NOTE 2 this needs to be a require and needs to be after the jest. Making it
// an import will fail
const notificationService = require('../../services/NotificationService').default;
describe("The notification viewer", () => {
beforeAll( ()=> {
notificationService.__setCount(5);
});
it("should display the correct number of notifications", async()=> {
const tree = renderer
.create(
<NoticationsViewer/>
)
await delay();
const instance = tree.root;
// find by className the component
const component = instance.findByProps( { className: 'notificiations' });
const text = component.children[0];
export(text).toEqual("5 Notifications");
});
});
Mocked Service src\services\__mocks__\NotificationService.js
let count = 0;
export default {
__setCount(__count) {
count = _count;
},
async GetNotifications() {
console.warn("Good Job! Using Mock");
}
}
Matchers in Jest
These can be found [1]
Some of them for quick reference.
expect(value) expect.extend(matchers) expect.anything() expect.any(constructor) expect.arrayContaining(array) expect.assertions(number) expect.hasAssertions() expect.not.arrayContaining(array) expect.not.objectContaining(object) expect.not.stringContaining(string) expect.not.stringMatching(string | regexp) expect.objectContaining(object) expect.stringContaining(string) expect.stringMatching(string | regexp) expect.addSnapshotSerializer(serializer)