【问题标题】:jasmine: Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVALjasmine:在 jasmine.DEFAULT_TIMEOUT_INTERVAL 指定的超时时间内未调用异步回调
【发布时间】:2014-05-01 12:41:32
【问题描述】:

我有一个名为 requestNotificationChannel 的 Angular 服务:

app.factory("requestNotificationChannel", function($rootScope) {

    var _DELETE_MESSAGE_ = "_DELETE_MESSAGE_";

    function deleteMessage(id, index) {
        $rootScope.$broadcast(_DELETE_MESSAGE_, { id: id, index: index });
    };

    return {
       deleteMessage: deleteMessage
    };

});

我正在尝试使用 jasmine 对该服务进行单元测试:

"use strict";

describe("Request Notification Channel", function() {
    var requestNotificationChannel, rootScope, scope;

    beforeEach(function(_requestNotificationChannel_) {
        module("messageAppModule");

        inject(function($injector, _requestNotificationChannel_) {
            rootScope = $injector.get("$rootScope");
            scope = rootScope.$new();
            requestNotificationChannel = _requestNotificationChannel_;
        })

        spyOn(rootScope, '$broadcast');
    });


    it("should broadcast delete message notification", function(done) {

        requestNotificationChannel.deleteMessage(1, 4);
        expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
        done();       
    });
});

我阅读了 Jasmine 中的异步支持,但由于我对使用 javascript 进行单元测试还比较陌生,因此无法使其正常工作。

我收到一个错误:

Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL

我的测试执行时间过长(大约 5 秒)。

有人可以帮我提供我的代码的工作示例和一些解释吗?

【问题讨论】:

  • 处理事件通常在一个摘要循环中完成。尝试将 scope.$apply() 添加到您的测试中,而不是使用 Jasmine 的异步测试模式
  • 这不起作用。我添加了 scope.$apply();刚刚调用 requestNotificationChannel.deleteMessage(1, 4) 但我收到同样的错误...
  • async tests take longer to run than Jest expects 时出现同样的错误 - 在调试和检查变量时很常见。
  • 尝试使用较小的超时时间。使用 timeout = 5000 时出现此错误。我将其替换为 2000,它对我有用!
  • 把这个留在这里是为了帮助像我一样的人。在 docker 容器内运行测试时出现此错误。测试有时会毫无问题地通过,但有时会失败。我认为这是某种比赛条件,但不知道为什么。我意识到我有一个afterEach 步骤正在清除数据库(使用deleteMany 方法)。在beforeAll 方法中添加jest.setTimeout(30000); 似乎已经为我解决了这个问题 - 我猜因为数据库删除是一个网络调用(在条件内),它有时需要超过 3 秒并抛出。

标签: javascript angularjs unit-testing asynchronous jasmine


【解决方案1】:

在删除 scope 引用和函数参数后工作:

"use strict";

describe("Request Notification Channel", function() {
    var requestNotificationChannel, rootScope;

    beforeEach(function() {
        module("messageAppModule");

        inject(function($injector, _requestNotificationChannel_) {
            rootScope = $injector.get("$rootScope");
            requestNotificationChannel = _requestNotificationChannel_;
        })
        spyOn(rootScope, "$broadcast");
    });


    it("should broadcast delete message notification with provided params", function() {
        requestNotificationChannel.deleteMessage(1, 4);
        expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4} );
    });
});

【讨论】:

    【解决方案2】:

    在你的it 函数(下面代码中的done)中有一个参数将导致 Jasmine 尝试异步调用。

    //this block signature will trigger async behavior.
    it("should work", function(done){
      //...
    });
    
    //this block signature will run synchronously
    it("should work", function(){
      //...
    });
    

    done 参数的名称并不重要,它的存在才是最重要的。我因复制/意大利面太多而遇到了这个问题。

    Jasmine Asynchronous Support 文档指出,参数(上面名为 done)是一个回调,可以调用它让 Jasmine 知道异步函数何时完成。如果你从不调用它,Jasmine 将永远不会知道你的测试已经完成并且最终会超时。

    【讨论】:

    • describe 中的 args 也是如此(角度而言,您需要在 describe 中调用 inject 来执行此操作)
    • @MartinBliss 已记录在案,我刚刚建议进行编辑以参考文档:stackoverflow.com/suggested-edits/2434606
    • 对于将来遇到此问题的随机 Google 员工请注意:如果您使用 Protractor 并遇到此问题,则此答案不是您要寻找的 - Protractor 会自行调用回调。
    • 它解决了我的问题,它是由同一个cuplrit“copy/pasta”引起的
    • @Vincent 如果发生此错误,那么 Protractor 用户的问题是什么?
    【解决方案3】:

    这个错误也可能是由于在初始化服务/工厂或其他任何东西时遗漏了注入。例如,可以这样抛出:

    var service;
    beforeEach(function(_TestService_) {
        service = _TestService_;
    });
    

    要修复它,只需用注入包装函数以正确检索服务:

    var service;
    beforeEach(inject(function(_TestService_) {
        service = _TestService_;
    }));
    

    【讨论】:

      【解决方案4】:

      即使对于异步测试,在这种情况下也会出现超时,您可以通过增加限制超时值来评估异步 Jasmine 回调来解决此错误

      describe('Helper', function () {
          var originalTimeout;
      
          beforeEach(function() {
              originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
              jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000000;
          });
      
          afterEach(function() {
            jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
          });
      
          it('Template advance', function(doneFn) {
              $.ajax({
                  url: 'public/your-end-point.mock.json',
                  dataType: 'json',
                  success: function (data, response) {
                      // Here your expected using data
                      expect(1).toBe(1)
                      doneFn();
                  },
                  error: function (data, response) {
                      // Here your expected using data
                      expect(1).toBe(1)
                      doneFn();
                  }
              });
          });
      });
      

      来源:http://jasmine.github.io/2.0/introduction.html#section-42

      【讨论】:

      • 这似乎不是“正确的方法”,但是在为我的 Selenium 测试添加了几个额外的零之后,这是一个必要的 hack。
      • 原来的jasmine.DEFAULT_TIMEOUT_INTERVAL是60000毫秒。所以这个例子实际上会缩短六倍。
      • 你说得对,我只是在这个例子中放了一个随机数,谢谢:)
      【解决方案5】:

      如果您在 it 函数中有参数 (done),请尝试将其删除,因为它会在函数本身中调用:

      it("should broadcast delete message notification", function(/*done -> YOU SHOULD REMOVE IT */) {
      
          requestNotificationChannel.deleteMessage(1, 4);
          expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
          // done(); -> YOU SHOULD REMOVE IT        
      });
      

      【讨论】:

      • 没有任何解释为什么这个答案没有那么有用。
      • 这个答案解决了我的问题...角度测试是一场噩梦!
      【解决方案6】:

      这个错误对我来说是出乎意料的,在一个一直有效的测试中。在我注意到我的 Macbook 运行缓慢之前,我找不到任何有用的建议。我注意到 CPU 被我杀死的另一个进程挂住了。 Jasmine 异步错误消失了,我的测试再次正常。

      别问我为什么,我不知道。但在我的情况下,这似乎是缺乏系统资源造成的。

      【讨论】:

      • 可能,当您的 CPU 空闲时,任务在默认超时之前完成。当 CPU 很忙时,您正在测试的任务需要很长时间才能完成。
      【解决方案7】:

      当您期待 beforeAll 函数中的某些内容时,您也会收到此错误!

      describe('...', function () {
      
          beforeAll(function () {
              ...
      
              expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
          });
      
          it('should successfully ...', function () {
      
          }
      }
      

      【讨论】:

        【解决方案8】:
        import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';
        

        使用 fakeAsync

        beforeEach(fakeAsync (() => {
        
        //your code
        
        }));
        
        
        
        describe('Intilalize', () => {
                it('should have a defined component', fakeAsync(() => {
                    createComponent();
                    expect(_AddComponent.ngOnInit).toBeDefined();
                }));
            });
        

        【讨论】:

          【解决方案9】:

          在我的例子中,这个错误是由于“fixture.detectChanges()”的使用不当引起的。这个方法似乎是一个事件监听器(异步),它只会在检测到变化时响应回调。如果未检测到更改,则不会调用回调,从而导致超时错误。希望这会有所帮助:)

          【讨论】:

            【解决方案10】:

            这与其说是一个答案,不如说是一种观察,但它可能会帮助像我一样沮丧的其他人。

            我在套件中的两个测试中不断收到此错误。我以为我只是用我正在做的重构破坏了测试,所以在撤销更改后不起作用,我恢复到早期的代码,两次(两次修订)认为它会摆脱错误。这样做没有任何改变。我昨天一整天都在追我的尾巴,今天早上还有一部分时间没有解决问题。

            我很沮丧,今天早上在笔记本电脑上检查了代码。运行整个测试套件(大约 180 个测试),没有错误。所以错误永远不会出现在代码或测试中。回到我的开发箱并重新启动它以清除内存中可能导致问题的任何内容。没有变化,在相同的两个测试中出现相同的错误。所以我从我的机器上删除了该目录,然后将其检出。瞧!没有错误。

            不知道是什么原因造成的,也不知道如何修复它,但删除工作目录并检查它是否已修复。

            希望这对某人有所帮助。

            【讨论】:

            • 谢谢你,我快疯了。我重新启动了我的电脑,就是这样
            • 就我而言,我只是重新运行了命令,它解决了这个问题。我对单元测试进行了热重载,并且每次都失败了。我不得不停止并再次运行命令。
            【解决方案11】:

            正如@mastablasta 所指出的,但还要补充一点,如果您调用“完成”参数或者更确切地说将其命名为完成,您只需在测试完成后调用回调完成()。

            // this block signature will trigger async behavior.
            it("should work", function(done){
              // do stuff and then call done...
              done();
            });
            
            // this block signature will run synchronously
            it("should work", function(){
              //...
            });
            

            【讨论】:

              【解决方案12】:

              茉莉花.DEFAULT_TIMEOUT_INTERVAL = 100000;

              将其保留在块中解决了我的问题。

              it('', () => {
               jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
              });
              

              【讨论】:

                【解决方案13】:

                您可以使用karma-jasmine 插件全局设置默认超时间隔。

                在 karma.conf.js 中添加这个配置

                module.exports = function(config) {
                  config.set({
                    client: {
                      jasmine: {
                        timeoutInterval: 10000
                      }
                    }
                  })
                }
                

                【讨论】:

                  【解决方案14】:

                  我所做的是:添加/更新了以下代码:

                  framework: 'jasmine',
                  jasmineNodeOpts: 
                  {
                      // Jasmine default timeout
                      defaultTimeoutInterval: 60000,
                      expectationResultHandler(passed, assertion) 
                      {
                        // do something
                      },
                  }
                  

                  【讨论】:

                  • 请解释为什么这段代码有效,而不是在没有解释的情况下发布它。
                  • 所以基本上当你执行一个测试并且它需要比预期更长的时间时,它会失败,因为满足默认超时并且脚本没有在执行中前进。这可能是由于某些条件未满足(例如可见性、页面加载)而发生的。现在,如果您的默认超时时间像 1000 毫秒 >> 脚本会经常失败,因为它只有一秒钟,并且可能有多种因素会导致脚本失败。但是,增加超时间隔可能会使浏览器/驱动程序等待更长的时间来满足条件。
                  • 好的,现在把它写进你的帖子;你应该尽量避免只用代码回答而不解释:)
                  【解决方案15】:

                  代替

                  beforeEach(() => {..
                  

                  使用

                  beforeEach(fakeAsync(() => {..
                  

                  【讨论】:

                    【解决方案16】:

                    不要使用done,只需将函数调用留空即可。

                    【讨论】:

                    • 如果我错了,请纠正我,但据我所知,它只是使测试套件在测试之前完成,并且错误消息被转储。这意味着任何失败的断言都不会破坏测试,因为测试套件在断言运行之前完成。这也可能意味着(我已经看到类似的行为)另一个测试显示了这个测试创建的错误。最后,这意味着一切看起来都不错,但随着测试数量的增加,问题会间歇性地出现。
                    【解决方案17】:

                    看起来测试正在等待一些永远不会到来的回调。这可能是因为测试没有以异步行为执行。

                    首先,看看是否只是在你的“it”场景中使用 fakeAsync:

                    it('should do something', fakeAsync(() => {
                    

                    您也可以使用flush() 等待微任务队列完成或使用tick() 等待指定的时间。

                    【讨论】:

                      【解决方案18】:

                      在我的例子中,超时是因为注入 providedIn: 'root' 的服务失败。目前尚不清楚注入失败的原因,也不清楚如果显然没有可用的提供程序实例,为什么没有早期错误。

                      我可以通过手动提供一个值来解决它:

                      TestBed.configureTestingModule({
                        declarations: [
                          // ...
                        ],
                        imports: [
                          // ...
                        ],
                        providers: [
                          // ...
                          { provide: MyService, useValue: { /* ... */ } },
                        ]
                      }).compileComponents();
                      

                      【讨论】:

                        【解决方案19】:

                        我发现了同样的错误,因为我在组件中使用了setTimeout 函数。示例:

                          ngOnInit(): void {
                            this.changeState();
                          }
                          
                          private changeState(): void {
                            setTimeout(() => this.state = StateEnum.IN_PROGRESS, 10000);
                          }
                        

                        当我将timeout 从 10000ms 更改为 0 或小于 5000ms (DEFAULT_TIMEOUT_INTERVAL) 时,所有测试都通过了。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2015-05-26
                          • 2017-07-24
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-04-21
                          • 2019-09-21
                          • 2018-11-21
                          相关资源
                          最近更新 更多