首页 文章

Nodejs - Mocha,Chai多重异步测试

提问于
浏览
1

在这里完成NodeJS测试noob . 试图单独测试通过我的API调用的函数(意思是,而不是向特定 endpoints 发出http请求,这通常会调用多个函数,而这些函数又向不同的第三方API发出请求,我想单独测试函数本身) . 他们为每个数据源(数据源=第三方API)构建一个类的方式,每个类包含具有相同确切签名的相同函数 - getDataconvertData ,并返回带有结果的回调 .

我还创建了一个创建许多 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);
            })
        });
     });
});

});`

这是有效的(或者至少测试在查询有效时通过,这是我想要的),但它很麻烦(非常)远非优雅或有效,特别是使用超时而不是使用promises,某种类型的异步或者可能是我还不熟悉的另一种选择 .

由于我找到的大多数资源(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测试而不是特定的异步函数,我希望从更有经验的Nodeer获得一些输入/最佳实践技巧 .

1 回答

  • 0

    您需要知道何时完成一堆异步操作 . 测试的优雅方法是使用promises和promise聚合:

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

    使用bluebirddataSources 包装到承诺中 . 您不需要修改自己测试的代码,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 ) .

相关问题