在对单元测试显示模块时如何存根私有函数

时间:2016-03-16 17:02:13

标签: javascript node.js unit-testing mocha sinon

我一直在构建一个节点模块,它包含了对GitHub API的大量调用,并且我的无限智慧是使用显示模块模式构建的,保持我的包装函数是私有的,只暴露简单的方法。请参阅以下示例:

github.shortcuts = (function(){

  var appPath;

  var createRepo = function(name){
    var deferred = Q.defer();
    github.repos.create({
      name: name,
      auto_init: true
    }, function(error, result){
      if (error) {
        deferred.reject(new Error(error));
      } else {
        deferred.resolve(result);
      }
    });
    return deferred.promise;
  };

  var updateRef = function(result){
    var deferred = Q.defer();
    var user = result.user;
    var repo = result.repo;
    github.gitdata.updateReference({
      user: user,
      repo: repo,
      ref: 'heads/master',
      sha: result.sha
    }, function(error, result){
      if (error) {
        deferred.reject(new Error(error));
      } else {
        deferred.resolve(result);
      }
    });
    return deferred.promise;
  };

  return {
    init: function(token, name, path){
      var deferred = Q.defer();
      appPath = path;

      var error = function(error){
        return deferred.reject(error);
      };

      github.authenticate({
        type: "oauth",
        token: token
      });

      createRepo(name)
        .then(updateRef, error)
        .then(function(result){
          deferred.resolve(result);
        }, error);

      return deferred.promise;
    }
  };

})();

然而,为此编写单元测试会引起我的问​​题。我不想测试我的私有函数,只测试公共函数init(),但是我想要保留私有函数,以便测试不会调用GitHub API。我使用Mocha和Chai进行测试,使用Sinon进行我的spys / stubs。

关于如何存根这些函数的任何建议或者如果这是一个糟糕的模式,我将如何测试这个模块将不胜感激!

3 个答案:

答案 0 :(得分:6)

你应该隔离类中有问题的部分并将其嘲笑,而不是将私有方法存根。模拟或存根私有方法的需要是一种设计气味,即IMO,表明该类太大,您应该将各种问题分开。

在这种情况下,您可以将github API作为参数传递给init,而不是通过类的内部挖掘,只需提供一个返回静态结果的假API。

当您开始测试错误情况时,这尤其有用,因为您的模拟API可以抛出相应的错误并允许您仅测试错误处理行为。

答案 1 :(得分:3)

实际上,有许多非常有效的理由要求您提供私有函数。就像它里面有很多逻辑,你想要测试那个逻辑。要删除任何私有函数,就像你将任何其他函数一样,然后将as any附加到父对象:

Spec.ts:

startTimeoutTimerSpy = spyOn(service as any, 'startTimeoutTimer');

服务具有私有功能startTimeoutTimer。将as any附加到服务对象会告诉TypeScript忽略任何类型,只是假设你可以这样做。

答案 2 :(得分:1)

由于github看起来像单身,你可以覆盖它的功能:

github.gitdata.updateReference = sinon.stub().return(Promise.resolve([]));

测试完成后你必须重置它:

afterAll(() => {
 github.gitdata.updateReference.reset();
});