我如何对在 Node.js 中使用承诺和事件发射器的函数进行单元测试?
How can I unit test a function that uses promises and event emitters in Node.js?
我的问题是关于 Node.js 中的承诺和事件发射器的单元测试。如果重要的话,我正在使用茉莉花框架。
下面的代码使用 Node.js 的 https 模块向 API 发送请求。 API 将 return JSON。 API 中的 JSON 是下面代码中的 "rawData" 变量。
我想对函数 returns JSON(而不是 JavaScript 对象)进行单元测试。
我尝试了多种方法对该功能的这方面进行单元测试,但均未成功:
1) 我尝试监视 Promise 构造函数,这样它就会 return 一个假函数,它只是 return 一个 JSON 字符串。
2) 我曾尝试监视 Node.js 中 EventEmitters 的 .on('eventType', callback) 函数来伪造一个 returns JSON 的函数。
我的问题是:上述两种方法中的任何一种都可以 and/or 推荐用于实现我的目标吗?是否有不同的方法来隔离 http 请求并从我的单元测试 objective 中发出事件?我是否需要重写此函数以便于更轻松地进行单元测试?
const https = require('https');
function getJSON() {
return new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
})
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
return json;
})
}
我认为你没有成功,因为你就这样直接返回。它应该是这样的:
function getJSON(callback) {
(new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
}))
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
callback(json);
})
}
// to use this:
getJSON((your_json)=> {
// handling your json here.
})
您可以使用 child_process 生成测试服务器以提供 JSON API。示例:
const { spawn } = require('child_process');
const expect = chai.expect;
const env = Object.assign({}, process.env, { PORT: 5000 });
const child = spawn('node', ['test-api.js'], { env });
child.stdout.on('data', _ => {
// Make a request to our app
getJSON((foo)=>{
// your asserts go here.
expect(foo).to.be.a('object');
expect(foo.some_attribute).to.be.a('string')
// stop the server
child.kill();
});
});
您可以在测试环境中自定义 someConfig
变量以指向“http://127.0.0.1:5000”。您的 test-api.js 文件是一个简单的 nodejs 脚本,它始终响应每个请求的预期 JSON。
更新单元测试示例
我会说您需要稍微重构代码以使其更易于测试。
当我为函数编写单元测试时,我牢记以下几点
您不需要测试内置或库模块,因为它们已经过良好测试。
始终重构您的函数,使其具有非常具体的职责。
在你的例子中实现这两个,我会在一个服务模块中分离服务器调用,该模块的唯一职责是进行url(和配置,如果有的话)进行服务器调用。
现在,当你这样做时,你会得到两个好处
1. 你有一段可重用的代码,你现在可以用它来进行其他服务器调用(也使你的代码更简洁和更短)
- 由于它是一个模块,您现在可以为该模块编写单独的测试,并负责检查服务器调用是否是从使用它的当前模块发出的。
现在在你的 getJSON 函数中剩下要测试的就是监视那个服务模块并使用 tohaveBeenCalledWith 并检查数据是否正确 parsed.You 可以将服务模拟到 return你想要的数据。
1 正在拨打服务电话
所以测试 toHaveBeenCalledWith
2 其解析为 JSON
所以测试 valid/invalid JSON
还测试失败
//no need to test whether https is working properly
//its already tested
const https = require('https');
const service = require("./pathToservice");
function getJSON() {
return service.get(somConfig)
.then(json => {
JSON.parse(json);
return json;
})
}
//its cleaner now
//plus testable
您是否有理由坚持 https
提出请求?如果没有,您的代码和测试都会变得非常简单。我将使用 axios.
举个例子
Http 请求可以像这样
getJSON() {
const url = 'https://httpbin.org/get';
return axios
.get(url)
.then(response => response);
}
你可以用 Sinon
存根 get
调用
lab.experiment('Fake http call', () => {
lab.before((done) => {
Sinon
.stub(axios, 'get')
.resolves({ data: { url: 'testUrl' } });
done();
});
lab.test('should return the fake data', (done) => {
const result = requestHelper.getJSON2();
result.then((response) => {
expect(response.data.url).to.eqls('testUrl');
axios.get.restore();
done();
});
});
});
使用现有代码,nock
会像这样工作
lab.experiment('Fake http call with nock', () => {
lab.test('should return the fake data', (done) => {
nock('https://httpbin.org')
.get('/get')
.reply(200, {
origin: '1.1.1.1',
url: 'http://testUrl',
});
const result = requestHelper.getJSON2();
result.then((response) => {
const result = JSON.parse(response);
console.log(JSON.parse(response).url);
expect(result.url).to.eqls('http://testUrl');
nock.cleanAll();
done();
});
});
});
完整代码是here
我的问题是关于 Node.js 中的承诺和事件发射器的单元测试。如果重要的话,我正在使用茉莉花框架。
下面的代码使用 Node.js 的 https 模块向 API 发送请求。 API 将 return JSON。 API 中的 JSON 是下面代码中的 "rawData" 变量。
我想对函数 returns JSON(而不是 JavaScript 对象)进行单元测试。
我尝试了多种方法对该功能的这方面进行单元测试,但均未成功:
1) 我尝试监视 Promise 构造函数,这样它就会 return 一个假函数,它只是 return 一个 JSON 字符串。
2) 我曾尝试监视 Node.js 中 EventEmitters 的 .on('eventType', callback) 函数来伪造一个 returns JSON 的函数。
我的问题是:上述两种方法中的任何一种都可以 and/or 推荐用于实现我的目标吗?是否有不同的方法来隔离 http 请求并从我的单元测试 objective 中发出事件?我是否需要重写此函数以便于更轻松地进行单元测试?
const https = require('https');
function getJSON() {
return new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
})
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
return json;
})
}
我认为你没有成功,因为你就这样直接返回。它应该是这样的:
function getJSON(callback) {
(new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
}))
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
callback(json);
})
}
// to use this:
getJSON((your_json)=> {
// handling your json here.
})
您可以使用 child_process 生成测试服务器以提供 JSON API。示例:
const { spawn } = require('child_process');
const expect = chai.expect;
const env = Object.assign({}, process.env, { PORT: 5000 });
const child = spawn('node', ['test-api.js'], { env });
child.stdout.on('data', _ => {
// Make a request to our app
getJSON((foo)=>{
// your asserts go here.
expect(foo).to.be.a('object');
expect(foo.some_attribute).to.be.a('string')
// stop the server
child.kill();
});
});
您可以在测试环境中自定义 someConfig
变量以指向“http://127.0.0.1:5000”。您的 test-api.js 文件是一个简单的 nodejs 脚本,它始终响应每个请求的预期 JSON。
更新单元测试示例
我会说您需要稍微重构代码以使其更易于测试。
当我为函数编写单元测试时,我牢记以下几点
您不需要测试内置或库模块,因为它们已经过良好测试。
始终重构您的函数,使其具有非常具体的职责。
在你的例子中实现这两个,我会在一个服务模块中分离服务器调用,该模块的唯一职责是进行url(和配置,如果有的话)进行服务器调用。
现在,当你这样做时,你会得到两个好处 1. 你有一段可重用的代码,你现在可以用它来进行其他服务器调用(也使你的代码更简洁和更短)
- 由于它是一个模块,您现在可以为该模块编写单独的测试,并负责检查服务器调用是否是从使用它的当前模块发出的。
现在在你的 getJSON 函数中剩下要测试的就是监视那个服务模块并使用 tohaveBeenCalledWith 并检查数据是否正确 parsed.You 可以将服务模拟到 return你想要的数据。
1 正在拨打服务电话 所以测试 toHaveBeenCalledWith
2 其解析为 JSON 所以测试 valid/invalid JSON 还测试失败
//no need to test whether https is working properly
//its already tested
const https = require('https');
const service = require("./pathToservice");
function getJSON() {
return service.get(somConfig)
.then(json => {
JSON.parse(json);
return json;
})
}
//its cleaner now
//plus testable
您是否有理由坚持 https
提出请求?如果没有,您的代码和测试都会变得非常简单。我将使用 axios.
Http 请求可以像这样
getJSON() {
const url = 'https://httpbin.org/get';
return axios
.get(url)
.then(response => response);
}
你可以用 Sinon
存根get
调用
lab.experiment('Fake http call', () => {
lab.before((done) => {
Sinon
.stub(axios, 'get')
.resolves({ data: { url: 'testUrl' } });
done();
});
lab.test('should return the fake data', (done) => {
const result = requestHelper.getJSON2();
result.then((response) => {
expect(response.data.url).to.eqls('testUrl');
axios.get.restore();
done();
});
});
});
使用现有代码,nock
会像这样工作
lab.experiment('Fake http call with nock', () => {
lab.test('should return the fake data', (done) => {
nock('https://httpbin.org')
.get('/get')
.reply(200, {
origin: '1.1.1.1',
url: 'http://testUrl',
});
const result = requestHelper.getJSON2();
result.then((response) => {
const result = JSON.parse(response);
console.log(JSON.parse(response).url);
expect(result.url).to.eqls('http://testUrl');
nock.cleanAll();
done();
});
});
});
完整代码是here