jasmine:异步回调未在jasmine.DEFAULT_TIMEOUT_INTERVAL指定的超时内调用


139

我有一个叫的服务requestNotificationChannel

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
    };

});

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

"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秒)。

有人可以帮助我提供一些可行的代码示例吗?


1
处理事件通常在摘要循环中完成。尝试在您的测试中添加scope。$ apply(),而不要使用Jasmine的异步测试模式
Eitan Peer 2014年

这行不通。我添加了scope。$ apply(); 刚刚在调用requestNotificationChannel.deleteMessage(1、4)之后,但是我收到了同样的错误...
Mdb

异步测试花费的时间比Jest预期的长时,我会收到相同的错误-在调试和花一些时间检查变量时很常见。
Dan Dascalescu

尝试使用较小的超时时间。使用超时= 5000时出现此错误。我将其替换为2000,它对我有用!
玛丽亚·里亚兹

1
离开这里来帮助我的鞋子的人。在docker容器中运行测试时遇到此错误。测试有时会通过而没有任何问题,但有时会失败。我认为这是某种竞赛情况,但不知道为什么。我意识到我有一个afterEach步骤要清除数据库(使用该deleteMany方法)。添加jest.setTimeout(30000);beforeAll方法似乎已经解决了我的问题-我猜是因为数据库删除是网络调用(在条件内),所以有时要花费3秒钟以上的时间才能抛出。
恩希尔

Answers:


230

在函数中使用参数itdone在下面的代码中)将导致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 异步支持文档注意到,参数(done上面命名)是一个回调,可以调用该回调以让Jasmine知道异步函数何时完成。如果您从未调用过它,Jasmine将永远不会知道您的测试已完成,并且最终会超时。


3
对于describe中的args同样如此(在角度上,您需要调用inject in describe来做到这一点)
Narretz 2014年

@MartinBliss据记录,我刚刚建议对文档进行修改,以参考该文档:stackoverflow.com/suggested-edits/2434606
Vincent

39
请注意,将来有随机的Google员工会遇到此问题:如果您使用的是Protractor并遇到此问题,则此答案不是您想要的-Protractor会自行调用回调。
文森特

它解决了我的问题,它是由相同的cuplrit“ copy / pasta”引起的
shaikh

1
@Vincent量角器用户会遇到什么问题,如果发生此错误?
布鲁诺·比耶里

57

即使对于异步测试,在这种情况下也会发生超时,您可以通过增加限制超时的值来评估异步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


1
这似乎不是“正确的方法”,但是在为我的Selenium测试添加了几个额外的零后,这是一个必要的技巧。
艾默里(Emery)

原始的jasmine.DEFAULT_TIMEOUT_INTERVAL为60000毫秒。因此,该示例实际上将其缩短了六倍。
瓦塔里(Waltari)'18 -10-17

您是对的,在这个示例中我只输入了一个随机数,谢谢:)
gsalgadotoledo 18-10-17

20

初始化服务/工厂或其他方法时未插入注入也可能导致此错误。例如,可以通过执行以下操作将其抛出:

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

要解决此问题,只需将函数与ject一起包装即可正确检索服务:

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

13
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();
        }));
    });

6

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

在karma.conf.js中添加此配置

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

5

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

不要问我为什么,我不知道。但是在我的情况下,似乎缺少系统资源。


5
可能是在CPU空闲时,任务在默认超时之前完成。当CPU繁忙时,您正在测试的任务花费了太长时间才能完成。
Shane

5

这更多的是观察,而不是答案,但它可能会对像我一样沮丧的其他人有所帮助。

我一直从套件中的两个测试中得到此错误。我以为我只是用我的重构破坏了测试,所以在撤消更改不起作用之后,我又恢复到早期的代码,两次(返回两次修订)都认为它可以消除错误。这样做并没有改变。昨天整天和今天早晨的一部分时间,我都追不着尾巴,但仍未解决问题。

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

不知道是什么原因造成的,或者如何修复它,但是删除工作目录并将其检出并修复它是什么。

希望这对某人有帮助。


1
谢谢你,我为此感到疯狂。我重新启动了PC,就是这样
yngrdyn

就我而言,我只是重新运行了命令,它解决了这个问题。每当单元测试失败时,我都会进行热装。我不得不停止并再次运行命令。
jignesh

4

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


如果我错了,请纠正我,但据我所知,它只会使测试套件在完全测试之前完成,而错误消息将转储。这意味着任何失败的断言都不会破坏测试,因为在运行断言之前测试套件已经完成。这也可能意味着(我已经看到类似的行为)另一个测试显示了该测试所创建的错误。最后,这意味着一切似乎都可以开始,但是随着测试数量的增加,问题会间歇性地弹出。
LosManos

3

当您期望beforeAll函数中有某些内容时,也会出现此错误!

describe('...', function () {

    beforeAll(function () {
        ...

        expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
    });

    it('should successfully ...', function () {

    }
}

2

以我为例,此错误是由于不当使用“ fixture.detectChanges()”引起的。似乎此方法是事件侦听器(异步),仅在检测到更改时才响应回调。如果未检测到更改,它将不会调用回调,从而导致超时错误。希望这可以帮助 :)


2

删除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} );
    });
});

0

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

// 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(){
  //...
});

0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

将其保留在区块中可以解决我的问题。

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

0

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

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

3
请说明该代码为何起作用,而不是不加说明地将其发布。
神户

因此,基本上,当您执行测试且所需时间比预期长时,​​测试将失败,因为达到了默认超时并且脚本没有在执行中向前移动。可能由于某些条件不满足而发生这种情况(例如,可见性,页面加载)。现在,如果您的默认超时时间是1000ms >>,脚本将经常失败,因为它只有一秒钟,并且可能有多种因素导致脚本失败。但是,增加超时间隔会使浏览器/驱动程序等待更长的时间,以满足条件。
Zeeshan

2
好的,现在在您的帖子中说出来;您应该尽量避免只回答没有解释的代码:)
Kobe


0

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

首先,查看是否仅在“ it”场景中使用了fakeAsync:

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

您还可以flush()用于等待microTask队列完成或tick()等待指定的时间。


-2

如果您doneit函数中有一个参数(),请尝试将其也从函数本身中删除:

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        
});

4
没有解释为什么这个答案没有那么有用。
加里

2
这个答案解决了我的问题...角度测试是一场噩梦!
本杰明·考尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.