Javascript 在单元测试模块时,如何存根私有函数

Javascript 在单元测试模块时,如何存根私有函数,javascript,node.js,unit-testing,mocha.js,sinon,Javascript,Node.js,Unit Testing,Mocha.js,Sinon,我一直在构建一个节点模块,它封装了对GitHub API的许多调用,并且以我无限的智慧,使用揭示模块模式构建了这个模块,使我的包装函数保持私有,并且只公开简单的方法。请参见下面的示例: github.shortcuts = (function(){ var appPath; var createRepo = function(name){ var deferred = Q.defer(); github.repos.create({ name: name,

我一直在构建一个节点模块,它封装了对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。我用摩卡和柴做测试,用锡诺做间谍/存根


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

由于
github
看起来像一个单体,您可以覆盖它的函数:

github.gitdata.updateReference = sinon.stub().return(Promise.resolve([]));
测试完成后,您必须重置它:

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

您应该隔离类中有问题的部分并模拟它,而不是存根私有方法。IMO,模拟或存根私有方法的需要是一种设计味道,表明类太大了,应该将各种关注点分开

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


当您开始测试错误案例时,这尤其有用,因为您的模拟API可以抛出适当的错误,并且只允许您测试错误处理行为。

实际上,有很多非常有效的原因,您可能希望将私有函数分包出去。就像里面有很多逻辑,你想测试一下逻辑。要删除任何私有函数,请像删除任何其他函数一样将其删除,然后将
作为任何
附加到父对象:

规格:

startTimeoutTimerSpy = spyOn(service as any, 'startTimeoutTimer');
服务有一个专用功能
startTimeoutTimer
。将
作为任何
附加到服务对象会告诉TypeScript忽略任何键入,并假设您可以这样做