У меня есть модуль для целей тестирования обучения, который выглядит следующим образом:
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
const URI_USERS = 'users/';
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => fetchUser(id)));
return users.map(user => parseUser(user));
}
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
Довольно простые вещи.
Теперь я хочу протестировать этот метод fetchUserStrings
, и для этого я хочу издеваться над шпионами над fetchUser
и parseUser
.В то же время - я не хочу, чтобы поведение parseUser
оставалось насмешливым, - когда я действительно проверяю это.
Я столкнулся с проблемой, заключающейся в том, что не представляется возможным издеваться / шпионить за функциями в одном модуле.
Вот ресурсы, о которых я читал:
Как смоделировать конкретную функцию модуля?Выпуск Jest Github. (100+ палец вверх).
где нам говорят:
Поддержка вышеперечисленного путем насмешки над функцией после запроса модуля невозможна в JavaScript - нет (почти) способа получить привязку, которая fooссылается и изменяет его.
Способ работы jest-mock заключается в том, что он выполняет код модуля изолированно, а затем извлекает метаданные модуля и создает фиктивные функции.Опять же, в этом случае не будет никакого способа изменить локальную привязку foo.
Обратитесь к функциям через объект
Решение, которое он предлагает, это ES5 - носовременный эквивалент описан в этом сообщении в блоге:
https://luetkemj.github.io/170421/mocking-modules-in-jest/
Где вместо прямого вызова моих функций я обращаюсь к ним через объект, подобный:
api.js
async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
async function fetchUsers() {
return lib.makeApiCall(URI_USERS);
}
async function fetchUser(id) {
return lib.makeApiCall(URI_USERS + id);
}
async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return users.map(user => lib.parseUser(user));
}
function parseUser(user) {
return `${user.name}:${user.username}`;
}
const lib = {
makeApiCall,
fetchUsers,
fetchUser,
fetchUserStrings,
parseUser
};
export default lib;
Другие посты, которые предлагают это решение:
https://groups.google.com/forum/#!topic/sinonjs/bPZYl6jjMdg https://stackoverflow.com/a/45288360/1068446
И этот кажетсябыть вариантом той же идеи: https://stackoverflow.com/a/47976589/1068446
Разбить объект на модули
Альтернативой является то, что я бы разбил свой модуль так, чтобы никогда не вызывать функции непосредственно внутридруг друга.
например.
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
user-api.js
import {makeApiCall} from "./api";
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
user-service.js
import {fetchUser} from "./user-api.js";
import {parseUser} from "./user-parser.js";
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return ids.map(user => lib.parseUser(user));
}
user-parser.js
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
И таким образом я могу смоделировать модули зависимостей при тестировании зависимыхМодуль, не беспокойтесь.
Но я не уверен, что такое разделение модулей даже возможно - я полагаю, что может возникнуть ситуация, когда у вас есть циклические зависимости.
Есть несколько альтернатив:
Внедрение зависимостей в функцию:
https://stackoverflow.com/a/47804180/1068446
Это выглядит ужасно, как будто, imo.
Использовать плагин babel-rewire
https://stackoverflow.com/a/52725067/1068446
Должен признаться - я не особо на это смотрел.
Разделите ваш тест на несколько файлов
Сейчас изучаю этот тест.
Мой вопрос: Это все довольно разочаровывающий и сложный способ тестирования - есть ли стандартный, приятный и простой способ, которым люди пишут юнит-тесты в 2018 году, которые специально решают эту проблему?