【问题标题】:$rootScope.digest not completing a promise in Jasmine$rootScope.digest 未完成 Jasmine 中的承诺
【发布时间】:2015-09-22 13:14:52
【问题描述】:

我们目前正在尝试测试我们的 Angular 服务,该服务使用 Promise 将值返回给控制器。问题是我们附加到 .then 的函数在 Jasmine 中没有被调用。

我们发现在返回 Promise 后向函数添加 $rootScope.digest() 可以调用同步 Promise,但它仍然不适用于异步 Promise。

到目前为止的代码是

    beforeEach(inject(function (Service, $rootScope)
    {
        service = Service;
        root = $rootScope;
    }));

    it('gets all the available products', function (done)
    {
        service.getData().then(function (data)
        {
            expect(data).not.toBeNull();
            expect(data.length).toBeGreaterThan(0);
            done();
        });
        root.$digest();
    });

在这种情况下,promise 可以正常调用,但如果它是异步的,则不会调用它,因为在调用 root.$digest() 时,promise 还没有准备好进行“消化”。

有没有什么方法可以告诉我什么时候承诺在解决之后才能调用摘要?或者可能会自动执行此操作?谢谢;)

我们必须测试的部分服务(删除错误处理):

var app = angular.module('service', []);

/**
 * Service for accessing data relating to the updates/downloads
 * */
app.factory('Service', function ($q)
{
     ... init

    function getData()
    {
        var deffered = $q.defer();

        var processors = [displayNameProc];

        downloads.getData(function (err, data)
        {
            chain.process(processors, data, function (err, processedData)
            {
                deffered.resolve(processedData);
            });
        });

        return deffered.promise;
    }
    ...

在出现问题的情况下,当 service.getData 为异步时,promise 已解决,但由于 root.$digest 已被调用,因此未调用测试代码中附加到该 promise 的函数。希望这能提供更多信息

解决方法

var interval;
beforeEach(inject(function ($rootScope)
{
    if(!interval)
    {
        interval = function ()
        {
            $rootScope.$digest();
        };
        window.setInterval(interval, /*timing*/);
    }
}));

我最终使用上面的解决方法重复调用摘要,这样每一层承诺都有机会运行......它并不理想或漂亮,但它适用于测试......

【问题讨论】:

  • 您是否在嘲笑您的服务以及返回的承诺?
  • 不,我正在尝试测试实际服务。
  • 如果您正在测试实际服务并且它在内部使用 $http 服务,那么我相信当远程调用成功或失败时,angularjs 会启动摘要。
  • 是的,我认为可能是这种情况,但不幸的是,我们的大多数服务都没有使用 http 请求。目前我可以添加一个技巧来重复调用摘要,这有点解决这个问题,但这不是一个很好的解决方案。如果有某种机制可以判断承诺何时解决但未消化,那就太好了。
  • 您是否有机会发布更多代码或创建小提琴/plunk?

标签: javascript angularjs jasmine angular-promise


【解决方案1】:

你应该把你的断言移到我相信的之外:

beforeEach(inject(function (Service, $rootScope)
{
    service = Service;
    root = $rootScope;
}));

it('gets all the available products', function (done)
{
    var result;
    service.getData().then(function (data)
    {
        result = data;
        done();
    });

    root.$digest();

    expect(result ).not.toBeNull();
    expect(result .length).toBeGreaterThan(0);
});

更新

从下面的评论中分叉出plunkr,以展示如何测试异步调用。

为服务添加了 $timeout:

var app = angular.module('bad-service', []);

app.service('BadService', function ($q, $interval, $timeout)
{
    this.innerPromise = function(){
      var task = $q.defer();
      console.log('Inside inner promise');

      $timeout(function() {
        console.log('Inner promise timer fired');
        task.resolve();
      }, 500);
      // $interval(function(){

      // }, 500, 1);
      return task.promise;
    }
});

我在断言之前刷新 $timeout:

beforeEach(module('test'));


describe('test', function(){
  var service, root, $timeout;

    beforeEach(inject(function (Service, $rootScope, _$timeout_)
    {
      $timeout = _$timeout_;
        console.log('injecting');
        service = Service;
        root = $rootScope;
    }));

    /**
    Test one
    */
    it('Should work', function(done){
      console.log('Before service call');
      service.outerPromise().then(function resolve(){
        console.log('I should be run!');
        expect(true).toBeTruthy();
        done();
      });
      // You will have to use the correct "resolve" here. For example when using:
      // $httpbackend - $httpBackend.flush();
      // $timeout- $timeout.flush();
      $timeout.flush();

      console.log('After service call');
    });
});

【讨论】:

  • 不幸的是,这在我们的案例中不起作用,因为 getData 是异步的,这意味着结果在到达第一个 expect(result ).not.toBeNull(); 时将为 null;
  • 这个解决方案专门针对异步调用;你有没有测试过它是否不起作用? 'done();' 的组合和'root.$digest();'将确保您的异步调用在调用期望之前得到解决。
  • 是的,抱歉,我知道现在我要试一试
  • 刚刚试了一下,我认为这与我最初的相似,不幸的是它仍然不会调用 then 函数,因为在 service.getData 中有一个嵌套的 promise 调用,它没有被“消化”意味着外部永远不会被解决,所以会发生什么 1)getData 被调用 2)digest 被调用 3)promise 由于嵌套的promise 4)expect 被调用为null ... afaik 你的解决方案应该可以工作但在那里一定是发生了其他事情,我不明白。
  • 你能创建一个 plunkr 吗?然后我会看看可能是什么问题。
猜你喜欢
  • 1970-01-01
  • 2019-04-29
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 2017-08-11
相关资源
最近更新 更多