jasmine: асинхронный обратный вызов не был вызван в течение времени ожидания, указанного в jasmine.DEFAULT_TIMEOUT_INTERVAL
У меня есть угловой сервис под названием 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 с).
Может ли кто-нибудь помочь мне предоставить рабочий пример моего кода с некоторыми пояснениями?
21 ответ
Имея аргумент в вашем it
Функция заставит его сделать попытку асинхронного вызова.
//this block signature will trigger async behavior.
it("should work", function(done){
//...
});
//this block signature will run synchronously
it("should work", function(){
//...
});
Это не имеет значения, что done
аргумент назван, его существование - все, что имеет значение. Я столкнулся с этой проблемой из-за слишком большого количества копий / пасты.
Документы по асинхронной поддержке Jasmin отмечают этот аргумент done
выше) - это обратный вызов, который можно вызвать, чтобы сообщить Jasmine о завершении асинхронной функции. Если вы никогда не позвоните, Жасмин никогда не узнает, что ваш тест завершен, и в конечном итоге истечет время ожидания.
В качестве обходного пути вы можете увеличить ограничение времени ожидания для оценки асинхронного обратного вызова Jasmine.
describe('Helper', function () {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
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();
}
});
});
});
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();
}));
});
Эта ошибка также может быть вызвана отсутствием инъекции при инициализации сервиса / фабрики или чего-либо еще. Например, это можно сделать, выполнив это:
var service;
beforeEach(function(_TestService_) {
service = _TestService_;
});
Чтобы исправить это, просто оберните функцию с помощью inject, чтобы правильно получить сервис:
var service;
beforeEach(inject(function(_TestService_) {
service = _TestService_;
}));
Вы можете использовать плагин karma-jasmine, чтобы установить интервал ожидания по умолчанию во всем мире.
Добавьте этот конфиг в karma.conf.js
module.exports = function(config) {
config.set({
client: {
jasmine: {
timeoutInterval: 10000
}
}
})
}
Это скорее наблюдение, чем ответ, но оно может помочь другим, которые были так же разочарованы, как и я.
Я продолжал получать эту ошибку из двух тестов в моем наборе. Я думал, что просто сломал тесты с помощью рефакторинга, который я делал, поэтому после возврата изменений не сработал, я вернулся к более раннему коду, дважды (две ревизии назад), думая, что это избавит от ошибки. Это ничего не изменило. Я преследовал свой хвост весь день вчера и часть этого утра, не решая проблему.
Я расстроился и проверил код на ноутбуке этим утром. Запустил весь набор тестов (около 180 тестов), ошибок нет. Таким образом, ошибки никогда не были в коде или тестах. Вернулся к моей коробке разработчика и перезагрузил ее, чтобы очистить все, что в памяти могло вызвать проблему. Без изменений, те же ошибки на тех же двух тестах. Поэтому я удалил каталог со своего компьютера и проверил его обратно. Вуаля! Нет ошибок
Не знаю, что вызвало это, или как это исправить, но удаление рабочего каталога и его восстановление исправили, что бы это ни было.
Надеюсь, это кому-нибудь поможет.
Эта ошибка началась с чистого листа для меня, на тесте, который всегда работал. Я не мог найти никаких предложений, которые помогли, пока я не заметил, что мой Macbook работает медленно. Я заметил, что процессор был привязан другим процессом, который я убил. Асинхронная ошибка Jasmine исчезла, и мои тесты снова в порядке.
Не спрашивай меня почему, я не знаю. Но в моих обстоятельствах это было похоже на недостаток системных ресурсов по вине.
Вы также получаете эту ошибку, ожидая чего-то в beforeAll
функция!
describe('...', function () {
beforeAll(function () {
...
expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
});
it('should successfully ...', function () {
}
}
Похоже, что тест ожидает обратного вызова, которого никогда не происходит. Вероятно, потому что тест не выполняется с асинхронным поведением.
Во-первых, посмотрите, используется ли просто fakeAsync в вашем "его" сценарии:
it('should do something', fakeAsync(() => {
Вы также можете использовать flush()
дождаться завершения очереди microTask или tick()
ждать указанное время.
В моем случае эта ошибка была вызвана неправильным использованием "fixture.detectChanges()". Кажется, этот метод является прослушивателем событий (асинхронным), который будет отвечать на обратный вызов только при обнаружении изменений. Если никаких изменений не обнаружено, он не будет вызывать обратный вызов, что приведет к ошибке тайм-аута. Надеюсь это поможет:)
Работает после удаления 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} );
});
});
Что я сделал: добавил / обновил следующий код:
framework: 'jasmine',
jasmineNodeOpts:
{
// Jasmine default timeout
defaultTimeoutInterval: 60000,
expectationResultHandler(passed, assertion)
{
// do something
},
}
Как отмечает @mastablasta, но также добавим, что если вы вызываете аргумент 'done' или, скорее, называете его завершенным, вы просто вызываете callback 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(){
//...
});
Я обнаружил ту же ошибку, потому что использовал
setTimeout
функция в компоненте. Пример:
ngOnInit(): void {
this.changeState();
}
private changeState(): void {
setTimeout(() => this.state = StateEnum.IN_PROGRESS, 10000);
}
Когда я изменил
timeout
от 10000 мс до 0 или менее 5000 мс (DEFAULT_TIMEOUT_INTERVAL), все тесты пройдены.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
Хранение этого в блоке решило мою проблему.
it('', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Вместо того
beforeEach(() => {..
использовать
beforeEach(fakeAsync(() => {..
В моем случае тайм-аут был вызван неудачной инъекцией службы с
Мне удалось обойти это, вручную указав значение:
TestBed.configureTestingModule({
declarations: [
// ...
],
imports: [
// ...
],
providers: [
// ...
{ provide: MyService, useValue: { /* ... */ } },
]
}).compileComponents();
для кого-то, сталкивающегося с таким типом, я впервые писал тест, и это было как тестовый оператор, и тест не прошел с ошибкой тайм-аута.
fit('log in with Facebook ', async done => {
Исправление, которое я нашел, заключалось в добавленииdone()
в качестве последнего утверждения теста.
fit('log in with Facebook ', async done => {
.....
.....
done();
})
В моем случае я не возвращал значение из шпионского метода, поэтому столкнулся с ошибкой,
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();
});
});
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
});