单元测试模块,在promise解析时调用私有函数

时间:2017-03-04 11:34:36

标签: javascript node.js unit-testing chai

我是Node的单元测试的新手,并且在承诺方面遇到了障碍。我的模块通过ApiController执行搜索,然后ApiController返回一个promise。根据其解析状态,它将调用模块中的两个私有函数之一。

模块:

module.exports.process = function(req, res) {
  let searchTerm = req.query.searchValue;

  ApiController.fetchResults(searchTerm)
    .then((results) => onReturnedResults(results), (err) => onNoResults());

  function onReturnedResults(results) {
    req.app.set('searchResults', results);
    res.redirect('/results');
  }

  function onNoResults() {
    res.render('search/search', { noResultsFound: true });
  }
};

测试:

var res = {
  viewName: '', data : {},
  render: function(view, viewData) {
    this.view = view;
    this.viewData = viewData;
  }
};

var req = { query: { searchValue: 'doesnotexist' } }

describe('When searching for results that do not exist', function() {
  it('should display a no results found message', function(done) {
    let expectedResult = {
      noResultsFound: true;
    }

    SearchController.process(req, res);

    // Assert...  
    expect(res.viewData).to.be.equal(expectedResult);
    done();
  });
})

围绕此问题的最佳做法是什么?我如何模拟' fetchResults返回了promise,因此它实际上并没有从API中获取结果(同时仍在调用私有函数)?

1 个答案:

答案 0 :(得分:1)

如果方法的范围是闭包或者是模块专用的,那么就无法访问它们,只能间接验证。要提供fetchResults的测试实现,必须以某种方式公开它。

一种方法是require ApiController(这可能已经发生),然后使用proxyquiremockery来覆盖依赖关系。我曾与许多测试套件合作,他们只能通过修补require进行交互并提供测试依赖关系。使用此方法,测试套件成为一场噩梦。清理,取消修补,级联补丁,都会成为巨大的时间汇,如果有其他选择,会建议不要使用它。

另一种方法是烘焙将fetchResults实现更改为对象的功能。除了测试之外,fetchResults可配置还有助于将代码与未来的更改隔离开来,有助于在需要获取结果的方式发生变化时将process的影响降至最低:

var Results = (function() {
  return {
    process: function(req, res) {
      let searchTerm = req.query.searchValue;

      this.fetchResults(searchTerm)
      .then((results) => this.onReturnedResults(req, res, results), (err) =>  this.onNoResults(res)),

    fetchResults: ApiController.fetchResults,     

    onReturnedResults: function(req, res, results) {
      req.app.set('searchResults', results);
      res.redirect('/results');
    },

    onNoResults: function(res) {
      res.render('search/search', { noResultsFound: true });
    }
  };

})()

(我暂时没有使用javascript,所以我不确定对上述对象进行建模的最佳方法,但重要的是如果你的测试想要覆盖一个实现而不是必须公开实现,上面的代码应该这样做)

现在有一个结果对象,fetchResults方法以易于覆盖的方式公开

var fakeFetchResults = sinon.stub();
fakeFetchResults.return(aFakePromiseWithResultsToTriggerResultsCodePath);
Results.fetchResult = fakeFetchResults;

以上公开了返回功能和错误功能,它还允许对该功能进行隔离单元测试。

使用过程通过结果对象完成,您的控制器可以注册

Results.process