代码之家  ›  专栏  ›  技术社区  ›  Mr.C

如何对节点中使用承诺和事件发射器的函数进行单元测试。js?

  •  3
  • Mr.C  · 技术社区  · 7 年前

    我的问题是关于在节点中使用承诺和事件发射器进行单元测试。js。如果这很重要的话,我正在使用jasmine框架。

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

    我想对函数返回JSON(而不是JavaScript对象)进行单元测试。

    我尝试了几种方法来对该函数的这一方面进行单元测试,但都没有成功:

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

    2) 我已经试着监视了。节点中EventEmitters的('eventType',callback)函数。js来伪造返回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;
      })
    }
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   AbhinavD    7 年前

    你有没有坚持的理由 https 为了提出请求?否则,您的代码和测试都会变得非常简单。我将使用 axios

    Http请求可以如下所示

    getJSON() {
    const url = 'https://httpbin.org/get';
    return axios
      .get(url)
      .then(response => response);
    

    }

    您可以将 get 使用呼叫 Sinon

     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

        2
  •  1
  •   khawar jamil    7 年前

    我想说的是,您需要对代码进行一点重构,使其更易于测试。

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

    1. 您不需要测试内置模块或库模块,因为它们已经过良好的测试。

    2. 始终重构您的函数,使其具有非常特定的响应能力。

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

    现在,当你这样做的时候,你会得到两个好处 1、您有一段可重用的代码,现在可以用来进行其他服务器调用(也可以使您的代码更简洁)

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

    现在,在getJSON函数中剩下的所有测试就是监视该服务模块,并使用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
    
        3
  •  0
  •   Hank Phung    7 年前

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

    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\u进程生成一个测试服务器来提供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 '。你的 测试api。js公司 文件是一个简单的nodejs脚本,它总是为每个请求响应预期的JSON。

    更新的单元测试示例