我有一个用于学习测试的模块,如下所示:
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
and parseUser
。同时 - 我不想要这样的行为parseUser
保持嘲笑 - 当我实际测试时。
我遇到的问题是,似乎不可能模拟/监视同一模块中的函数。
以下是我读过的有关它的资源:
如何模拟特定模块功能?笑话 github 问题。 https://github.com/facebook/jest/issues/936(100+ 竖起大拇指)。
我们被告知:
在 JavaScript 中,通过在请求模块后模拟函数来支持上述功能是不可能的——(几乎)没有办法检索 foo 引用的绑定并修改它。
jest-mock 的工作方式是单独运行模块代码,然后检索模块的元数据并创建模拟函数。同样,在这种情况下,它将无法修改 foo 的本地绑定。
通过对象引用函数
他提出的解决方案是 ES5 - 但这篇博文中描述了现代的等效解决方案:
https://luetkemj.github.io/170421/mocking-modules-in-jest/ 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://groups.google.com/forum/#!topic/sinonjs/bPZYl6jjMdg
https://stackoverflow.com/a/45288360/1068446 https://stackoverflow.com/a/45288360/1068446
这似乎是同一想法的变体:https://stackoverflow.com/a/47976589/1068446 https://stackoverflow.com/a/47976589/1068446
将对象分解为模块
另一种方法是,我将分解我的模块,这样我就不会直接在彼此内部调用函数。
eg.
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;
}
}
用户-api.js
import {makeApiCall} from "./api";
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
用户服务.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));
}
用户解析器.js
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
这样我就可以在测试依赖模块时模拟依赖模块,不用担心。
但我不确定像这样分解模块是否可行 - 我想可能存在循环依赖的情况。
有一些替代方案:
函数中的依赖注入:
https://stackoverflow.com/a/47804180/1068446 https://stackoverflow.com/a/47804180/1068446
在我看来,这个看起来很难看。
使用 babel-rewire 插件
https://stackoverflow.com/a/52725067/1068446 https://stackoverflow.com/a/52725067/1068446
我必须承认——我还没有看过这么多。
将您的测试拆分为多个文件
现在正在调查这个。
我的问题:这是一种相当令人沮丧且繁琐的测试方式 - 2018 年人们编写单元测试的方式是否有一种标准、好用且简单的方式来专门解决这个问题?