Jasmine spyOn没有正确模拟函数

时间:2017-09-04 21:07:37

标签: javascript angularjs unit-testing jasmine

所以我有这样的功能:

        function initializeView() {
            var deferred = $q.defer();
            var deleteOrUploadToBestMoments = this.deleteOrUploadToBestMoments;
            var checkAndDeleteExpiredMoments = this.checkAndDeleteExpiredMoments;

            getNearbyMoments()
            .then(deleteOrUploadToBestMoments)
            .then(checkAndDeleteExpiredMoments).then(function(moments) {
                //omitted
                }, function(error) {
                    deferred.reject(error);
                });
            return deferred.promise;
        };

我想用单元测试来测试这个功能。这是我的测试:

it('Should call initializeView', function(done) {
    spyOn(service, 'getNearbyMoments').and.callFake(function() {
        console.log("MOCKED getNearbyMoments");
        return $q.resolve(mockOutMoments());
    });
    spyOn(service, 'deleteOrUploadToBestMoments').and.callFake(function() {
        console.log("MOCKED deleteOrUploadToBestMoments");
        return $q.resolve();
    });
    spyOn(service, 'checkAndDeleteExpiredMoments').and.callFake(function() {
        console.log("MOCKED checkAndDeleteExpiredMoments");
        return $q.resolve();
    });
    service.initializeView().then(function(moments) {
        //omitted
        done();
    });
    $scope.$apply();
});

问题出在spyOn函数上。它并没有嘲弄正确的功能。但是,如果我在生产代码中的getNearbyMoments前面添加一个'this',它会正确地模拟它:

   function initializeView() {
        var deferred = $q.defer();
        var deleteOrUploadToBestMoments = this.deleteOrUploadToBestMoments;
        var checkAndDeleteExpiredMoments = this.checkAndDeleteExpiredMoments;

        this.getNearbyMoments()
        .then(deleteOrUploadToBestMoments)
        .then(checkAndDeleteExpiredMoments).then(function(moments) {
            //omitted
            }, function(error) {
                deferred.reject(error);
            });
        return deferred.promise;
    };

我可以在我的生产代码中的所有函数调用前面添加一个'this',但当'this'的上下文发生变化时,这就成了一个问题。另外,我不需要改变我的生产代码只是为了让我的测试变得快乐 - 这让我真的想弄清楚我在测试中做错了什么,所以我没有对一个潜在的巨大问题进行创可贴

编辑:

显然我正在使用'this'正确地做到这一点。但问题是每当我这样做时,我都会收到错误

'TypeError: this.getNearbyMoments is not a function`

继承我的代码:

function initializeView() {
            var deferred = $q.defer();
            var deleteOrUploadToBestMoments = this.deleteOrUploadToBestMoments;
            var checkAndDeleteExpiredMoments = this.checkAndDeleteExpiredMoments;
            var getNearbyMoments = this.getNearbyMoments;
            this.getNearbyMoments()
            .then(deleteOrUploadToBestMoments)
            .then(checkAndDeleteExpiredMoments).then(function(moments) {
                //omitted
                }, function(error) {
                    console.log("ERROR");
                    console.log(error);
                    deferred.reject(error);
                });
            return deferred.promise;
        };

对于某些上下文,我的服务看起来像这样:

(function() {
    angular.module('app.momentsService', [])

    .service('momentsService', ['core', '$q', 'constants', 'logger', 'geolocation', 'awsServices', 'localStorageManager', momentsService]);

    function momentsService(core, $q, constants, logger, geolocation, awsService, localStorageManager){

//Lots of other functions
        this.checkAndDeleteExpiredMoments = checkAndDeleteExpiredMoments;
        this.getNearbyMoments = getNearbyMoments;
        this.deleteOrUploadToBestMoments = deleteOrUploadToBestMoments;

        function getNearbyMoments() {
//Omitted...
       };
function initializeView() {
//Well, you already know whats in here...
};

所以我很困惑,因为我在服务的顶部清楚地定义了函数,但由于某种原因它不知道它存在。

4 个答案:

答案 0 :(得分:1)

  

spyOn(service, 'getNearbyMoments').and.callFake(function() { console.log("MOCKED getNearbyMoments"); return $q.resolve(mockOutMoments()); });

由于spyOn的机制,无法修改对getNearbyMoments()的调用。 initializeView函数中的引用不能被spyOn以特定方式覆盖。

您必须在this前面添加getNearbyMoments,以便使用spyOn

否则,您可以通过公开公开引用getNearbyMoments并修改它来实现您想要的行为。但是你基本上会重新实现整个模拟系统做一些比添加this更复杂的事情。

答案 1 :(得分:0)

您的代码没有显示这一点,但我假设您可能正在执行类似

的操作
var getNearbyMoments = this.getNearbyMoments;

在某个地方,因为你试图窥探service.getNearbyMoments,但实际上并没有通过附加到服务的那个名称来调用函数。 如果就是这种情况,我的猜测是这是一个javascript引用问题。 spyOn的工作原理是这样的(但更复杂):

function(obj, method) {
  var originalFn = obj[ method ];
  obj[ method ] = function() {
    // do spy stuff
    // and potentially call the original
    // depending what you do with the spy
    originalFn()
  }
}

因为您已将原始函数分配给它自己的变量,所以当该函数被spyOn包装时,它不再指向相同的引用。

在将函数重新分配给局部变量之前,您需要先使用this(或service)或监视函数。同样,没有看到更多的代码使得这很难评估。

有关此参考问题的证明,请尝试:

var obj = {
  foo: function() {}
};
var foo = obj.foo;
spyOn(obj, 'foo')
foo();
expect(obj.foo).to.haveBeenCalled() // -> false

关于this不同的问题。 。 。我总是定义这样的服务:

angular.module('app').factory('SomeService', function() {
  var service = {
    foo: function() {
      service.bar();
    },

    bar: function() {
      console.log('bar');
    }
  };
  return service;
});

这样,服务中的所有内容都使用本身对自身的引用,而这种引用永远不会改变。

答案 2 :(得分:0)

那个怎么样=这个?

代码中的

        ...         那=上下文         that.getNearbyMoments()         。然后(deleteOrUploadToBestMoments)

并且在测试中:

...
 that=service
 service.initializeView()

答案 3 :(得分:0)

如果你这样做

this.getNearbyMoments()...

initializeView()内,您需要更改

function getNearbyMoments() {
  //Omitted...
};

this.getNearbyMoments = function() {
  //Omitted...
};