【问题标题】:jasmine test does not move into the then part of the promise茉莉花测试没有进入承诺的 then 部分
【发布时间】:2018-02-12 06:26:36
【问题描述】:

我想测试一下这个功能:

    function initializeView() {
        var deferred = $q.defer();
        if(this.momentArray) {
            core.listMoments(constants.BEST_MOMENT_PREFIX, '').then(function(moments) {
                //Ommitted
                deferred.resolve(moments);      
            }, function(error) {
                console.log("ERROR");
                deferred.reject(error);
            });
        }
        else {
            deferred.resolve();
        }
        return deferred.promise;    
    };

函数调用core.listMoments:

    function listMoments(prefix, startAfter) {
        // var deferred = $q.defer();
        var promises = [];
        return awsServices.getMoments(prefix, startAfter).then(function(moments) { //Mocked
            console.log("getMoments Returned"); //Does not print
            for(var i = 0; i < moments.length; i++) {
                // moments[i].Key = constants.IMAGE_URL + moments[i].Key;
                promises.push(getMomentMetaData(moments[i]));
            }
        return $q.all(promises);
        });
    };

这是我的测试函数:

it('Should correctly initialize the view', function(done) {
    spyOn(awsServices, 'getMoments').and.callFake(function() {
        console.log("getMoments Has been mocked"); //This prints
        return $q.resolve(mock_moment);
    });
    service.initializeView().then(function() {
        done();
    })
});

问题在于 awsServices 的“getMoments”模拟。对 awsServices.getMoments 的调用位于 listMoments 函数中。我想模拟这个函数,但是当我这样做时,它并没有执行承诺的“then”部分。

因此,根据我的控制台日志,它会打印“getMoments 已被模拟”日志,但不会打印“getMoments Returned”日志。所以这个函数被模拟了,但由于某种原因它没有进入 then 语句,我的测试只是超时了。

【问题讨论】:

  • getMoments 将返回mock_moments,但在getMomentsgetMomentslistMoments 函数中,它将每个时刻数据传递给getMomentMetaData(我怀疑你还需要模拟这个方法,如果它正在执行实际的 ajax)。
  • 你的权利,我确实需要嘲笑它,但它现在甚至没有得到它。
  • 你可以直接模拟core.listMoments来验证initializeView函数的行为。并单独测试listMoments
  • 我可以,不过我想把它作为一个集成测试。

标签: javascript angularjs unit-testing jasmine


【解决方案1】:

$q 承诺可以是同步的(当它们被同步解析时)并且依赖于摘要周期。

Angular 测试中通常不应该有异步 done 回调。

Angular 测试应该是同步的,$q 承诺也是如此。为了实现当现有的承诺(从getMomentsinitializeView 返回的承诺)与then 链接时,应手动触发摘要。如果done 回调被放置在then 中并且没有触发摘要,这将导致规范超时。

spyOn(awsServices, 'getMoments').and.callFake(function() {
    console.log("getMoments Has been mocked"); //This prints
    return $q.resolve(mock_moment);
});
service.initializeView();
$rootScope.$digest();

这里可以改进的是隔离。单个测试涉及多个单元(方法)。当其中一个发生故障时,这将影响故障排除。

通常单元测试意味着一次只测试一个单元,而其余的则被模拟或存根。在这种情况下,在一个测试中调用service.listMoments 并模拟awsServices.getMoments,而在另一个测试中调用service.initializeView 并模拟service.listMoments

【讨论】:

    【解决方案2】:

    为了让承诺的.then() 部分在这样的测试中起作用,您需要使用$rootScope.$apply()。无论承诺是在您的测试代码中还是在正在测试的引用库中,这都是必需的。可以将其想象为用于$http$timeout 调用的flush() 函数。

    Angular documentation's $q page 的测试示例展示了如何使用它:

    it('should simulate promise', inject(function($q, $rootScope) {
      var deferred = $q.defer();
      var promise = deferred.promise;
      var resolvedValue;
    
      promise.then(function(value) { resolvedValue = value; });
      expect(resolvedValue).toBeUndefined();
    
      // Simulate resolving of promise
      deferred.resolve(123);
      // Note that the 'then' function does not get called synchronously.
      // This is because we want the promise API to always be async, whether or not
      // it got called synchronously or asynchronously.
      expect(resolvedValue).toBeUndefined();
    
      // Propagate promise resolution to 'then' functions using $apply().
      $rootScope.$apply();
      expect(resolvedValue).toEqual(123);
    }));
    

    注意他们注入了$rootScope

    【讨论】:

    猜你喜欢
    • 2023-03-27
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多