Jasmine: 在 jasmine.DEFAULT_TIMEOUT_INTERVAL 指定的超时内没有调用 Async 回调

我有一个角度服务叫 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
};


});

我正在尝试使用 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

而且我的测试执行时间太长(大约5s)。

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

273671 次浏览

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

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 异步支持文档指出,参数(上面命名为 done)是一个回调函数,可以调用它来让 Jasmine 知道异步函数何时完成。如果您从不调用它,Jasmine 将永远不会知道您的测试已经完成,并且最终会超时。

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

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

要修复这个问题,只需要将函数包装成注入,以正确地检索服务:

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

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

如果你在 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
});

对我来说,这个错误是在一个一直有效的测试中出现的。我找不到任何有用的建议,直到我注意到我的 Macbook 运行缓慢。我注意到中央处理器被另一个进程钉住了,我杀死了它。Jasmine 异步错误消失了,我的测试再次恢复正常。

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

当在 beforeAll函数中期待某些内容时,您也会得到这个错误!

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


beforeAll(function () {
...


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


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


}
}
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';

使用 伪造异步

beforeEach(fakeAsync (() => {


//your code


}));






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

在我的案例中,这个错误是由于不正确地使用“ fixture.dettChanges ()”造成的。看起来这个方法是一个事件侦听器(异步) ,它只会在检测到更改时响应一个回调。如果未检测到更改,则不会调用回调,从而导致超时错误。希望这对你有帮助:)

这更像是一种观察,而不是一个答案,但是它可能会帮助那些和我一样沮丧的人。

我的套件中的两个测试一直出现这个错误。我认为我只是用我正在进行的重构破坏了测试,所以在退出更改不起作用之后,我又回到了早期的代码,两次(两次修订后)认为它可以消除错误。这么做并没有改变什么。我昨天追了我的尾巴一整天,今天早上的一部分没有解决这个问题。

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

不知道是什么引起的也不知道怎么修复但是删除工作目录再检查就修复了。

希望这对谁有帮助。

正如@masablasta 所指出的,还要补充的是,如果您调用“ done”参数,或者更确切地说,将其命名为 完成,那么在测试完成时,您只需要在测试中调用回调补全()。

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

DEFAULT _ TIMEOUT _ INTERVAL = 100000;

把这个藏起来解决了我的问题。

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

您可以使用 因果报应茉莉花插件设置默认的全局超时间隔。

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

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

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

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

而不是

beforeEach(() => {..

使用

beforeEach(fakeAsync(() => {..

不要使用 done,只要保持函数调用为空即可。

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

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

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

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

在我的例子中,超时是由于使用 providedIn: 'root'注入服务失败造成的。目前还不清楚为什么注入失败,也不清楚为什么在显然没有提供者实例可用的情况下没有早期错误。

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

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

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

  ngOnInit(): void {
this.changeState();
}
  

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

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

在我的例子中,我没有返回谍方法的值因此遇到了错误,

        mainMethod(args): Observable<something>{
return nestedMethod().pipe();
}

您的测试应该如下,

        it('your test case', (done: DoneFn) => {
const testData = {}; // Your data
spyOn(service, 'nestedMethod').and.returnValue(of(testData));
const obxValue = service.mainMethod('your args');


obxValue.pipe(first()).subscribe((data) => {
expect(data).not.toBeUndefined();
done();
});
});