茉莉花测试不会进入承诺的那部分

时间:2017-09-04 03:23:09

标签: javascript angularjs unit-testing jasmine

我想测试这个功能:

    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函数中。我想模仿这个函数,但是当我这样做时,它不会执行promise的“then”部分。

因此,基于我的控制台日志,它将打印'getMoments Has mocked'日志,但它不会打印'getMoments Returned'日志。因此该函数被模拟,但由于某种原因,它没有进入then语句,我的测试只是超时。

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

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

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

角度测试应该是同步的,$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被模拟。