Comprehensive Guide to Unit Testing in React and React Native Projects
This article presents a complete tutorial on setting up and using Jest, Enzyme, and react‑hooks‑testing‑library for unit testing React and React Native applications, covering environment configuration, simple function tests, lifecycle hooks, mocking, snapshot testing, asynchronous tests, Redux and Hook testing, as well as coverage reporting and CI integration.
In large‑scale front‑end projects, testing is essential for maintaining code quality, and React's component model and functional programming style make it especially suitable for unit testing. This guide details a full solution for unit testing React and React Native projects.
1. Technology Stack : Jest, Enzyme, and react‑hooks‑testing‑library are selected for their simplicity, snapshot capabilities, and built‑in coverage reporting.
1.1 Jest – a Facebook‑maintained testing framework that works out‑of‑the‑box with React and React Native, offering easy configuration, snapshot testing, and Istanbul‑based coverage reports.
1.2 Enzyme – an AirBnB open‑source library that provides a concise API for rendering components, querying elements, and simulating interactions, complementing Jest for comprehensive component testing.
2. Environment Configuration
{
"devDependencies": {
"@testing-library/react-hooks": "^3.2.1",
"babel-jest": "^24.8.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"jest": "^24.8.0",
"jest-junit": "^7.0.0",
"jest-react-native": "^18.0.0",
"react-test-renderer": "16.9.0",
"redux-mock-store": "^1.5.3"
}
}A jest.config.js file is added to the project root:
module.exports = {
preset: 'react-native',
globals: { _window: {}, __DEV__: true },
setupFiles: ['./jest.setup.js'],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '
/__mocks__/fileMock.js',
'\\.(css|less|scss)$': '
/__mocks__/stylesMock.js'
},
transform: {
'^.+\\.js$': '
/node_modules/react-native/jest/preprocessor.js'
},
testMatch: ['**/__tests__/**/*.(spec|test).js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
collectCoverage: true,
collectCoverageFrom: ['src/**/*.js'],
coverageReporters: ['text-summary', 'json-summary', 'lcov', 'html', 'clover'],
testResultsProcessor: './node_modules/jest-junit',
transformIgnorePatterns: ['
/node_modules/(?!@ctrip|react-native)']
};3. Simple Function Test
function add(x, y) {
return x + y;
} describe('Util', () => {
test('should return 3', () => {
const x = 1;
const y = 2;
const output = 3;
expect(add(x, y)).toBe(output);
});
});Common Jest assertions such as toBe , toEqual , toBeTruthy , and toMatch are listed.
4. Lifecycle Hooks
beforeAll(() => { console.log('run before all tests'); });
afterAll(() => { console.log('run after all tests'); });
beforeEach(() => { console.log('run before each test'); });
afterEach(() => { console.log('run after each test'); });5. Mock Functions
jest.useFakeTimers();
jest.mock('./src/commons/CViewPort', () => props =>
{props && props.children}
);
jest.mock('./src/commons/CToast', () => ({ show: () => {} }));Manual mocks for React Native components (e.g., InteractionManager ) are also demonstrated.
6. UI Snapshot Testing
it('render List', () => {
const tree = renderer.create(
).toJSON();
expect(tree).toMatchSnapshot();
});Snapshots are stored automatically and updated with jest -u when intentional UI changes occur.
7. Asynchronous Testing
const cityInfo = { 1: 'Beijing', 2: 'Shanghai' };
export default function fetch(url, params) {
return new Promise((resolve, reject) => {
if (params.cityId && cityInfo[params.cityId]) {
resolve(cityInfo[params.cityId]);
} else {
reject('city not found');
}
}));
} it('test cityInfo', async () => {
expect.assertions(1);
const data = await fetch('/cityInfo', { cityId: 1 });
expect(data).toEqual('Beijing');
});8. Enzyme Component Testing
Three rendering strategies are described: shallow (isolated component), mount (full DOM), and render (static HTML).
const label = shallow(
);
label.childAt(0).find({ eventName: 'click filterLabel' }).simulate('press');
expect(onClickLabel).toBeCalled();Testing internal component methods with instance() and jest.spyOn is also shown.
9. Redux Testing
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { updateList } from '../pages/List/action';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('list action test', () => {
it('updateList test', () => {
const store = mockStore({ flist: {} });
const mockData = { flist: { afitem: 1 } };
const expectedActions = { type: 'UPDATE_LIST', flist: { afitem: 1 } };
expect(store.dispatch(updateList(mockData.flist))).toEqual(expectedActions);
});
});10. React‑Hooks Testing
Installation of @testing-library/react-hooks and examples for useState , useEffect , and custom hooks are provided.
// useCityName.js
export default function useCityName() {
const [cityName, setCityName] = useState('Beijing');
const format = useCallback(() => setCityName(x => x + ' City'), []);
return { cityName, format };
}
// useCityName.test.js
describe('test useCityName', () => {
it('should use cityname', () => {
const { result } = renderHook(() => useCityName());
expect(result.current.cityName).toBe('Beijing');
expect(typeof result.current.format).toBe('function');
});
it('should format cityname', () => {
const { result } = renderHook(() => useCityName());
act(() => { result.current.format(); });
expect(result.current.cityName).toBe('Beijing City');
});
});11. Coverage and CI Integration
Jest integrates Istanbul to produce statement, branch, function, and line coverage reports via jest --coverage . Husky hooks are configured to run tests before commits or pushes, ensuring that only passing tests are merged.
"scripts": {
"test": "jest --forceExit --silent"
},
"devDependencies": {
"husky": "^3.0.9"
},
"husky": {
"hooks": {
"pre-push": "npm run test"
}
}12. Conclusion
The tutorial demonstrates how a systematic unit‑testing strategy—covering component rendering, state management, hooks, mocks, snapshots, and coverage—improves code reliability, facilitates safe refactoring, and serves as living documentation for React and React Native projects at Ctrip.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.