当前位置: 首页 > 知识库问答 >
问题:

使用Jasmine 2.0在异步测试中获取“$digest已在进行中”

潘辰龙
2023-03-14

我知道在摘要周期中手动调用$digest$Application会导致$digest已经在进行中错误,但我不知道为什么我在这里得到它。

这是一个包装$超文本传输协议的服务的单元测试,该服务足够简单,它只是防止对服务器进行重复调用,同时确保试图进行调用的html" target="_blank">代码仍然获得预期的数据。

angular.module('services')
    .factory('httpService', ['$http', function($http) {

        var pendingCalls = {};

        var createKey = function(url, data, method) {
            return method + url + JSON.stringify(data);
        };

        var send = function(url, data, method) {
            var key = createKey(url, data, method);
            if (pendingCalls[key]) {
                return pendingCalls[key];
            }
            var promise = $http({
                method: method,
                url: url,
                data: data
            });
            pendingCalls[key] = promise;
            promise.then(function() {
                delete pendingCalls[key];
            });
            return promise;
        };

        return {
            post: function(url, data) {
                return send(url, data, 'POST');
            },
            get: function(url, data) {
                return send(url, data, 'GET');
            },
            _delete: function(url, data) {
                return send(url, data, 'DELETE');
            }
        };
    }]);

单元测试也非常简单,它使用$httpBackend来预期请求。

it('does GET requests', function(done) {
    $httpBackend.expectGET('/some/random/url').respond('The response');

    service.get('/some/random/url').then(function(result) {
        expect(result.data).toEqual('The response');
        done();
    });
    $httpBackend.flush();
});

当调用done()时出现“$digest ready in progress”错误,这将导致失败。我不知道为什么。我可以通过在这样的超时中包装done()来解决这个问题

setTimeout(function() { done() }, 1);

这意味着,done()将排队并在$digest完成后运行,但这解决了我的问题,我想知道

  • 为什么Angular首先处于消化周期?
  • 为什么调用do()会触发此错误?

我在Jasmine 1.3中运行了完全相同的测试,这只发生在我升级到Jasmine 2.0并重写测试以使用新的异步语法之后。

共有3个答案

鲜于峰
2023-03-14

添加到@deitch的答案中。为了使测试更加健壮,你可以在回调之前添加一个间谍。这应该保证你的回调实际上被调用。

it('does GET requests', function() {
  var callback = jasmine.createSpy().and.callFake(function(result) {
    expect(result.data).toEqual('The response');
  });

  $httpBackend.expectGET('/some/random/url').respond('The response');
  service.get('/some/random/url').then(callback);
  $httpBackend.flush();

  expect(callback).toHaveBeenCalled();
});
辛龙野
2023-03-14
匿名用户

@deitch是对的,$http pBacked.flush()触发摘要。问题是,当$http pBackend.verifyNoOutstaning期望();在每个完成后运行时,它也有一个摘要。这是事件的顺序:

  1. 您可以调用flush(),这将触发摘要
  2. 执行then()
  3. 执行done()
  4. verifyNoOutstandingExpectation()运行时会触发摘要,但您已经在摘要中,因此会出现错误

done()。如果then没有运行,那么您现在可能知道出现了故障。关键是在启动done()之前确保摘要已完成。

it('does GET requests', function(done) {
    $httpBackend.expectGET('/some/random/url').respond('The response');

    service.get('/some/random/url').then(function(result) {
        expect(result.data).toEqual('The response');
        setTimeout(done, 0); // run the done() after the current $digest is complete.
    });
    $httpBackend.flush();
});

done()放入超时将使其在当前摘要完成后立即执行()。这将确保您想要运行的所有预期的都将实际运行。

浦琪
2023-03-14

$httpBacked.flush()实际上启动并完成一个$digest()循环。我昨天花了一整天的时间来挖掘ngResource和angular Mock的来源,以弄清这一点,但仍然没有完全理解它。

据我所知,$httpBackend.flush()的目的是完全避免上面的异步结构。换句话说,it('should do something',function(done){})的语法$httpBackend.flush()不能很好地配合使用。.flush()的目的就是推送挂起的异步回调,然后返回。它就像一个大的done包装器,围绕着所有异步回调。

因此,如果我理解正确(现在它对我有效),正确的方法是在使用$httpBackend.flush()时删除done()处理器:

it('does GET requests', function() {
    $httpBackend.expectGET('/some/random/url').respond('The response');

    service.get('/some/random/url').then(function(result) {
        expect(result.data).toEqual('The response');
    });
    $httpBackend.flush();
});

如果您添加console.log语句,您会发现所有回调都在flush()循环中一致发生:

it('does GET requests', function() {
    $httpBackend.expectGET('/some/random/url').respond('The response');

    console.log("pre-get");
    service.get('/some/random/url').then(function(result) {
        console.log("async callback begin");
        expect(result.data).toEqual('The response');
        console.log("async callback end");
    });
    console.log("pre-flush");
    $httpBackend.flush();
    console.log("post-flush");
});

那么输出将是:

预购

预冲洗

异步回调开始

异步回调结束

后冲洗

每一次。如果你真的想看它,抓住范围,看看范围。$$phase

var scope;
beforeEach(function(){
    inject(function($rootScope){
        scope = $rootScope;
    });
});
it('does GET requests', function() {
    $httpBackend.expectGET('/some/random/url').respond('The response');

    console.log("pre-get "+scope.$$phase);
    service.get('/some/random/url').then(function(result) {
        console.log("async callback begin "+scope.$$phase);
        expect(result.data).toEqual('The response');
        console.log("async callback end "+scope.$$phase);
    });
    console.log("pre-flush "+scope.$$phase);
    $httpBackend.flush();
    console.log("post-flush "+scope.$$phase);
});

您将看到输出:

预先得到未定义

预冲洗未定义

异步回调开始$摘要

异步回调结束$digest

后冲洗未定义

 类似资料:
  • 问题内容: 尝试致电时出现此错误 错误是 问题答案: 复制: 防止在调用$ scope。$ apply()时发生错误$digest。 您收到的错误表明Angular的脏检查已经在进行中。 最新的最佳实践表明,如果要在下一个 摘要 迭代中执行任何代码,则应使用: 上一页反应: (不要使用此方法) 使用安全的申请,例如: 你为什么不反转条件?

  • 我正在尝试用Jasmine和RequireJS做一些测试。一切都进行得很好,直到我注意到我所描述的函数的上下文出现了问题。 有人知道怎么解决这个吗?

  • 所以,我在测试一个依赖于事件发射器的组件。为此,我想出了一个解决方案,使用mocha+chai的承诺: 在控制台上,我得到了一个“unhandledPromiserEjectionWarning”,尽管reject函数正在被调用,因为它会立即显示消息“assertionError:Promise Error" (Node:25754)UnhandledPromiseRejectionWarning

  • 如何在组件返回中使用promise函数,如下所示: 因此,结果将是: 即使我在同一个组件中有几个promise,我也必须使用?