Using a mock function
export function forEach(items, callback) {
for (let index = 0; index < items.length; index++) {
callback(items[index]);
}
}
- 콜백함수의 작동을 확인하고 싶은것이 목적이라면 forEach에 들어가는 콜백을 어디선가 가져오는 대신에, 간단한 목업함수를 작성해 빠르게 테스트를 진행할 수 있다.
const forEach = require('./forEach');
const mockCallback = jest.fn(x => 42 + x);
test('forEach mock function', () => {
forEach([0, 1], mockCallback);
expect(mockCallback.mock.calls).toHaveLength(2);
expect(mockCallback.mock.calls[0][0]).toBe(0);
expect(mockCallback.mock.calls[1][0]).toBe(1);
expect(mockCallback.mock.results[0].value).toBe(42);
});
.mock property
- mock 속성 내에 호출될 당시의 함수의 인스턴스, 인자, context, 결과값 등을 저장함
- mock 속성으로는 instances, contxts, calls, result가 있음
expect(someMockFunction.mock.calls).toHaveLength(1);
expect(someMockFunction.mock.calls[0][0]).toBe('first arg');
expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
expect(someMockFunction.mock.results[0].value).toBe('return value');
expect(someMockFunction.mock.contexts[0]).toBe(element);
expect(someMockFunction.mock.instances.length).toBe(2);
expect(someMockFunction.mock.instances[0].name).toBe('test');
expect(someMockFunction.mock.lastCall[0]).toBe('test');
const myMock1 = jest.fn();
const a = new myMock1();
console.log(myMock1.mock.instances);
const myMock2 = jest.fn();
const b = {};
const bound = myMock2.bind(b);
bound();
console.log(myMock2.mock.contexts);
Mock Return Values
- 목업함수에 특정한 값이 나오도록 지정해줄 수 있음
const myMock = jest.fn();
console.log(myMock());
myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock());
const filterTestFn = jest.fn();
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);
const result = [11, 12].filter(num => filterTestFn(num));
console.log(filterTestFn.mock.calls[0][0]);
console.log(filterTestFn.mock.calls[1][0]);
console.log(result);
- api에서 값을 받아오는 함수를 테스트하고 싶다.
import axios from 'axios';
class Users {
static all() {
return axios.get('/users.json').then(resp => resp.data);
}
}
export default Users;
- 실제 함수를 사용하지 않고, 가짜 데이터를 생성해 mockResolvedValue에 집어넣음으로써 api 호출을 목업화 할 수 있다.
import axios from 'axios';
import Users from './users';
jest.mock('axios');
test('should fetch users', () => {
const users = [{name: 'Bob'}];
const resp = {data: users};
axios.get.mockResolvedValue(resp);
return Users.all().then(data => expect(data).toEqual(users));
});
Mocking Partials
- 모듈 중에서 일부만 목업화하고 일부는 실제로 동작시키게도 할 수 있음
export const foo = 'foo';
export const bar = () => 'bar';
export default () => 'baz';
import defaultExport, {bar, foo} from '../foo-bar-baz';
jest.mock('../foo-bar-baz', () => {
const originalModule = jest.requireActual('../foo-bar-baz');
return {
__esModule: true,
...originalModule,
default: jest.fn(() => 'mocked baz'),
foo: 'mocked foo',
};
});
test('should do a partial mock', () => {
const defaultExportResult = defaultExport();
expect(defaultExportResult).toBe('mocked baz');
expect(defaultExport).toHaveBeenCalled();
expect(foo).toBe('mocked foo');
expect(bar()).toBe('bar');
});
Mock Implementations
- 목업 함수의 구현이 실제로 필요한 경우가 생길 수 있음
- 실제 구현을 위해 jest.fn 내부에 실제 함수 작성
const myMockFn = jest.fn(cb => cb(null, true));
myMockFn((err, val) => console.log(val));
module.exports = function () {
};
jest.mock('../foo');
const foo = require('../foo');
foo.mockImplementation(() => 42);
foo();
- 목업함수의 값이 그때그때 달라지는 경우 mockImplementationOnce 메서드 사용
const myMockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
myMockFn((err, val) => console.log(val));
myMockFn((err, val) => console.log(val));
const myMockFn = jest
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
- 메서드들이 엮여 있어 this를 반환해야 하는경우 mockReturnThis() 메서드 사용
const myObj = {
myMethod: jest.fn().mockReturnThis(),
};
const otherObj = {
myMethod: jest.fn(function () {
return this;
}),
};
Mock Names
- 목업 함수에 이름을 지어주어 테스트에 오류가 발생시 빠르게 디버깅을 진행할 수 있음
const myMockFn = jest
.fn()
.mockReturnValue('default')
.mockImplementation(scalar => 42 + scalar)
.mockName('add42');
Custom Matchers
- 목업함수에는 추가로 함수의 호출 횟수, 스냅샷, 선택인자, 이름과 관련된 메서드들이 있음
expect(mockFunc).toHaveBeenCalled();
expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);
expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);
expect(mockFunc).toMatchSnapshot();
expect(mockFunc.mock.calls.length).toBeGreaterThan(3);
expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]);
expect(mockFunc.getMockName()).toBe('a mock name');