如何在Node.js中对使用promises和事件发射器的函数进行单元测试?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Node.js中对使用promises和事件发射器的函数进行单元测试?相关的知识,希望对你有一定的参考价值。

我的问题是关于Node.js中的promises和事件发射器的单元测试。如果重要的话,我正在使用茉莉花框架。

下面的代码使用Node.js的https模块向API发送请求。 API将返回JSON。 API中的JSON是下面代码中的“rawData”变量。

我想单元测试该函数返回JSON(而不是javascript对象)。

我没有尝试过几种方法来测试这个功能的单元测试:

1)我试图监视Promise构造函数,以便它返回一个假函数,它只返回一个JSON字符串。

2)我试图监视Node.js中EventEmitters的.on('eventType',callback)函数,伪造一个返回JSON的函数。

我的问题是:上述两种方法中的任何一种都可能和/或建议实现我的目标吗?是否有不同的方法来隔离http请求和从我的单元测试目标中发出事件?我是否需要重写此功能以方便单元测试?

 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;
  })
}
答案

我会说你需要重构一下代码才能更加可测试。

当我为函数编写单元测试时,我会记住以下几点

  1. 您不需要测试内置模块或库模块,因为它们已经过良好测试。
  2. 始终重构您的职能以承担非常具体的责任。

在您的示例中实现这两个,我将在服务模块中分离服务器调用,该服务模块的唯一责任是使用url(和配置,如果有的话)进行服务器调用。

现在,当你这样做时,你会得到两个好处1.你有一个可重复使用的代码片段,你现在可以使用它来进行其他服务器调用(也使你的代码更清晰,更短)

  1. 由于它是一个模块,您现在可以为该模块编写单独的测试,并负责检查服务器调用是否来自使用它的当前模块。

现在,你在getJSON函数中测试的所有内容都是spyOn该服务模块并使用tohaveBeenCalledWith并检查数据是否被正确解析。你可以模拟服务以返回所需的数据。

1它进行服务调用,以便测试toHaveBeenCalledWith

2它解析为JSON所以测试有效/无效的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);

}

你可以用getSinon调用存根

 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

另一答案

我认为你没有成功,因为你是这样直接回来的。应该是这样的:

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。

更新了单元测试示例

以上是关于如何在Node.js中对使用promises和事件发射器的函数进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章

node.js异步控制流程 回调,事件,promise和async/await

使用 Node.Js 和 Promises,你如何返回一个已经实现的 Promise? [复制]

使用 Node.Js 和 Promises,你如何返回一个已经实现的 Promise? [复制]

when 让你跳出异步回调噩梦 node.js下promise/A规范的使用

如何使用 Promise 和 node.js 正确检查和记录 http 状态代码?

如何在 node.js 中将函数与 Promise 同步