本文作者:IMWeb zzbozheng 原文出处:IMWeb社区 未经同意,禁止转载
nock 是前端常用来模拟http请求响应的工具,它基于nodejs的原生http模块,并且他可以让我们写一些轻逻辑的代码,我们先看一个简单的例子:
nock('http://www.example.com')
.post('/login', 'username=pgte&password=123456')
.reply(200, { id: '123ABC' });
fetchUser('pgte', '123456');
上面的例子中fetchUser
会发出一个post请求到example.com/login
. Nock将会拦截这个请求并立即返回你预先定义好的响应。
当我第一次开始使用Nock时,我急切地开始使用它进行单元测试。 然而,我很快就感觉到我花了更多时间编写Nocks而不是实际测试业务逻辑。 对此的一个解决方案是将您的请求代码与业务逻辑分开。 我们来看一些例子吧:
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
// User does not exist
if (response.status === 404) return null;
// Some other error occurred
if (response.status > 400) {
throw new Error(`Unable to fetch user #${id}`);
}
const { firstName, lastName } = await response.json();
return {
firstName,
lastName,
fullName: `${firstName} ${lastName}`
};
}
向 /api/users/<用户ID>
发送请求,当处理完响应结果返回一个 firstName 和 lastName 的对象。 此功能的测试代码可能如下所示:
it('should properly decorate the fullName', async () => {
nock('http://localhost')
.get('/api/users/123')
.reply(200, { firstName: 'John', lastName: 'Doe });
const user = await getUser(123);
expect(user).toEqual({
firstName: 'John',
lastName: 'Doe,
fullName: 'John Doe'
});
});
it('should return null if the user does not exist', async () => {
nock('http://localhost')
.get('/api/users/1337')
.reply(404);
const user = await getUser(1337);
expect(user).toBe(null);
});
it('should return null when an error occurs', async () => {
nock('http://localhost')
.get('/api/users/42')
.reply(404);
const userPromise = getUser(42);
expect(userPromise).rejects.toThrow('Unable to fetch user #42');
});
上面的测试代码主要分为两部分:
// api.js
export async function getUserFromApi(id) {
const response = await fetch(`/api/users/${id}`);
// User does not exist
if (response.status === 404) return null;
// Some other error occurred
if (response.status > 400) {
throw new Error(`Unable to fetch user #${id}`);
}
return response.json();
}
// user.js
import { getUserFromApi } from './api';
async function getUserWithFullName(id) {
const user = await getUserFromApi(id);
if (!user) return user;
const { firstName, lastName } = user;
return {
firstName,
lastName,
fullName: `${firstName} ${lastName}`
};
}
var scope = nock(/\.qq\.com$/)
.get('/resource')
.reply(200, 'domain regex matched');
var scope = nock('http://www.example.com')
.get(/source$/)
.reply(200, 'path using regex matched');
var scope = nock('http://www.example.com')
.get(function(uri) {
return uri.indexOf('cats') >= 0;
})
.reply(200, 'path using function matched');
var scope = nock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(201, function(uri, requestBody) {
return requestBody;
});
var scope = nock('http://www.google.com')
.filteringRequestBody(/.*/, '*')
.post('/echo', '*')
.reply(201, function(uri, requestBody, cb) {
fs.readFile('cat-poems.txt' , cb); // Error-first callback
});
你也可以使用您选择的模拟库来模拟我们自己的API包装器,而不是使用Nock来模拟HTTP请求。 我更喜欢Jest
,但这种模式并不依赖于任何特定的模拟库。
import { getUserFromApi } from './api';
jest.mock('./api');
it('should properly decorate the fullName', async () => {
getUserFromApi.mockResolvedValueOnce(
{ firstName: 'John', lastName: 'Doe }
);
const user = await getUser(123);
expect(user).toEqual({
firstName: 'John',
lastName: 'Doe,
fullName: 'John Doe'
});
});
it('should return null if the user does not exist', async () => {
getUserFromApi.mockResolvedValueOnce(null);
const user = await getUser(1337);
expect(user).toBe(null);
});
这样的测试看起来更简洁。 所有HTTP开销现在都包含在API模块中。 我们已经最小化地完成了HTTP传输,最大限度地减少了使用 Nock 来测度 。