Функції-імітації
Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new, and allowing test-time configuration of return values.
There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.
Використання функцій-імітацій
Let's imagine we're testing an implementation of a function forEach, which invokes a callback for each item in a supplied array.
export function forEach(items, callback) {
for (const item of items) {
callback(item);
}
}
Щоб протестувати цю функцію, ми можемо використати функцію-імітацію і перевірити її стан, щоб переконатися, що зворо тній виклик був викликаний, як і очікувалося.
import {forEach} from './forEach';
const mockCallback = jest.fn(x => 42 + x);
test('forEach mock function', () => {
forEach([0, 1], mockCallback);
// The mock function was called twice
expect(mockCallback.mock.calls).toHaveLength(2);
// The first argument of the first call to the function was 0
expect(mockCallback.mock.calls[0][0]).toBe(0);
// The first argument of the second call to the function was 1
expect(mockCallback.mock.calls[1][0]).toBe(1);
// The return value of the first call to the function was 42
expect(mockCallback.mock.results[0].value).toBe(42);
});
.mock property
All mock functions have this special .mock property, which is where data about how the function has been called and what the function returned is kept. The .mock property also tracks the value of this for each call, so it is possible to inspect this as well:
const myMock1 = jest.fn();
const a = new myMock1();
console.log(myMock1.mock.instances);
// > [ <a> ]
const myMock2 = jest.fn();
const b = {};
const bound = myMock2.bind(b);
bound();
console.log(myMock2.mock.contexts);
// > [ <b> ]
Наступні властивості функцій-імітацій дуже корисні в тестах для перевірки того, як ці функції були викликані, які екземпляри були створені або які значення вони повернули:
// The function was called exactly once
expect(someMockFunction.mock.calls).toHaveLength(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');
// The return value of the first call to the function was 'return value'
expect(someMockFunction.mock.results[0].value).toBe('return value');
// The function was called with a certain `this` context: the `element` object.
expect(someMockFunction.mock.contexts[0]).toBe(element);
// 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).toBe('test');
// The first argument of the last call to the function was 'test'
expect(someMockFunction.mock.lastCall[0]).toBe('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 filterTestFn = jest.fn();
// Make the mock return `true` for the first call,
// and `false` for the second call
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);
const result = [11, 12].filter(num => filterTestFn(num));
console.log(result);
// > [11]
console.log(filterTestFn.mock.calls[0][0]); // 11
console.log(filterTestFn.mock.calls[1][0]); // 12
Більшість прикладів з реального життя передбачають створення функцій-імітацій в компонентах, від яких залежить ваш код, але техніка використовується та ж сама. В такому випадку намагайтеся уникати спокуси імплементувати логіку всередині будь-якої функції, яка безпосередньо не тестується.