2016-02-28 152 views
1

完成NodeJS测试noob在这里。尝试单独测试通过我的API调用的函数(意思是,而不是向特定的端点发出http请求,而这通常会调用几个函数,然后向不同的第三方API发出请求),我想单独测试函数)。他们被调用的方式是我为每个数据源(数据源=第三方API)构建了一个类,每个类都包含具有相同确切签名的相同函数 - getDataconvertData,并返回包含结果的回调。Nodejs - 摩卡,柴多个异步测试

我还创建用于创建许多user嘲笑,由于每个用户上下文返回不同的数据的模块(意思是,一个用户对象被送入getData,其中为了确定哪些数据应返回使用某些user特性) 。

我想测试的方式是创建大量模拟,然后为每个模拟运行函数。这是我到目前为止有:

// Data sources to iterate over. Each is a class instance acquired through "require". 
var dataSources = [ 
    source1, 
    source2, 
    source3, 
    source4 
]; 

describe('getData', function() {  
    this.timeout(10000); 
    describe('per data source,', function() { 
     context('standard call', function() { 

      // Associative array to hold the data returned, a key for each data source. 
      var finalResults = {}; 

      // Iterate over all data sources 
      _.forEach(dataSources, function(dataSource) { 

       // Generate user mocks 
       var users = userMocks(10); 

       // Iterate over all users. 
       _.forEach(users, function (user) { 

        // Call each data source with each of the users. 
        // Numbers of calls to make - (users * data-sources), so in this case - 10*4. 
        dataSource.getData(user, function (err, data) { 
         if (err) return done(err); 

         // Convert the data returned to my format 
         dataSource.convertData(data, function (err, processedData) { 
          if (err) return done(err); 

          // Populate finalResults with converted data from each source 
          if (finalResults[dataSource.sourceName]) { 
           finalResults[dataSource.sourceName] = finalResults[dataSource.sourceName].concat(processedData); 
          } else { 
           finalResults[dataSource.sourceName] = processedData; 
          } 
         }); 
        }); 
       }); 
      }); 

      it('should return something', function(done) { 
       _.forEach(finalResults.keys, function(key) { 
        expect(finalResults[key]).to.not.be.empty; 
        expect(finalResults[key].length).to.be.greaterThan(0); 
       }); 
       setTimeout(function() { 
        done(); 
       }, 10000); 
      }) 
     }); 
    }); 
}); 

});`

这工作(或至少测试通过查询的时候是有效的,这是我想要的东西),但它的繁琐和(非常)远离优雅或有效,特别是使用超时而不是使用承诺,某种异步,或者我可能还不熟悉的另一种选择。

由于大多数我发现的资源(http://alanhollis.com/node-js-testing-a-node-js-api-with-mocha-async-and-should/https://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/https://justinbellamy.com/testing-async-code-with-mocha/,只是仅举几例)讨论了直接的API测试,而不是具体的异步功能的,我希望得到更多的经验Noders一些输入/最佳实践技巧。

+0

在node.js中启动并拆除http srevers是相当容易的。而不是数据嘲讽,创建大量轻量级“模拟”数据服务器,对请求数据执行测试,并向应用程序提供“模拟”数据。你甚至可以在服务器和测试代码之间共享javascript执行上下文!这将使你的测试从“单元”到“集成”的规模稍微有所变化,但会让这些事情变得更加清洁。 – lxe

+0

@lxe我完全同意,没有涉及太多的细节,但这就是我基本上在做的,最终的结果是一个用户数组,但我使用一个服务来做到这一点(正在使用其他地方)。接下来的步骤是测试我自己的API来巩固所有这些第三方通话,这很像您所描述的。真的很感谢输入! – CodeBender

回答

0

您需要知道何时完成一堆异步操作。来测试优雅的方式是使用承诺,并承诺聚集:

Promise.all([ promise1, promise2, promise3 ]).then(function(results) { 
    // all my promises are fulfilled here, and results is an array of results 
}); 

包装你dataSources到使用bluebird一个承诺。你并不需要修改测试的代码自我,蓝鸟提供了方便的方法:

var Promise = require('bluebird') 
var dataSources = [ 
    source1, 
    source2, 
    source3, 
    source4 
].map(Promise.promisifyAll); 

使用新promisified功能,为每个呼叫建立承诺:

context('standard call', function() { 
     var finalResults = {}; 
     var promiseOfResults = datasources.map(function(dataSource) { 
      var users = userMocks(10); 
      // Promise.all will take an array of promises and return a promise that is fulfilled then all of promises are 
      return Promise.all(users.map(function(user) { 
       // *Async functions are generated by bluebird, via Promise.promisifyAll 
       return dataSource.getDataAsync(user) 
        .then(dataSource.convertDataAsync) 
        .then(function(processedData) { 
         if (finalResults[dataSource.sourceName]) { 
          finalResults[dataSource.sourceName] = finalResults[dataSource.sourceName].concat(processedData); 
         } else { 
          finalResults[dataSource.sourceName] = processedData; 
         } 
        }); 
      }); 
     }); 
     // promiseOfResults consists now of array of agregated promises 
     it('should return something', function(done) { 
      // Promise.all agregates all od your 'datasource' promises and is fulfilled when all of them are 
      // You don't need the promise result here, since you agegated finalResults yourself 
      return Promise.all(promiseOfResults).then(function() { 
       _.forEach(finalResults.keys, function(key) { 
        expect(finalResults[key]).to.not.be.empty; 
        expect(finalResults[key].length).to.be.greaterThan(0); 
       }); 
       done(); 
      }); 
     }); 

测试的其余部分应使用相同Promise.all(promiseOfResults),除非你需要新的结果。

+0

请原谅缺乏经验,但是如果函数已经构建为使用回调函数,那么这很重要吗?传递一个简单的错误处理函数是否会满足承诺? – CodeBender

+0

@CodeBender蓝鸟的promisification需要它的函数/对象有'callback(err,result)',所以是的,它确实很重要。换句话说,如果未构建'getData',则不会生成'getDataAsync',而是使用回调。测试代码无需承诺就可以做出测试代码,但它会显着变大并且不易读。 – Koder

+0

我不认为我的问题已经足够清楚了 - 不确定getDataAsync和convertDataAsync是从哪里来的 - 您能否详细说明这些问题?这只是我的'getData'和'convertData'函数的一个占位符,或者是自动生成的实际函数,或者是应该添加到每个数据源类的函数? – CodeBender