【问题标题】:Unit test module that invokes private functions upon promise resolve在 promise resolve 时调用私有函数的单元测试模块
【发布时间】:2017-03-04 11:34:36
【问题描述】:

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

模块:

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 返回的承诺,使其实际上不会从 API 获取结果(同时仍调用私有函数)?

【问题讨论】:

    标签: javascript node.js unit-testing chai


    【解决方案1】:

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

    一种方法是requireApiController(这可能已经发生),然后使用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 方法的Results 对象,该方法以易于覆盖的方式公开

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

    上面公开了返回函数和错误函数,这也允许对该功能进行独立的单元测试。

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

    Results.process

    【讨论】:

    • 谢谢,这真的很有帮助:)
    猜你喜欢
    • 2016-07-02
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    相关资源
    最近更新 更多