jasmine: асинхронный обратный вызов не был вызван в течение тайм-аута, указанного jasmine.DEFAULT_TIMEOUT_INTERVAL

147

У меня есть угловая служба 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 с).

Может ли кто-нибудь помочь мне предоставить рабочий пример моего кода с некоторыми пояснениями?

Mdb
источник
1
Обработка событий обычно выполняется в цикле дайджеста. Попробуйте добавить в тест scope. $ Apply () вместо использования шаблона асинхронного тестирования Жасмин
Эйтан Пир
это не сработало. Я добавил область. $ Apply (); сразу после вызова requestNotificationChannel.deleteMessage (1, 4), но я получаю ту же ошибку ...
Mdb,
Я получаю ту же ошибку, когда выполнение асинхронных тестов занимает больше времени, чем Jestожидалось - очень часто при отладке и необходимости времени для проверки переменных.
Дэн Даскалеску
Вместо этого попробуйте использовать меньший тайм-аут. Я получил эту ошибку при использовании timeout = 5000. Я заменил ее на 2000, и у меня она сработала!
Марина Риаз
1
Оставляю это здесь, чтобы помочь кому-то на моем месте. У меня была эта ошибка при выполнении тестов внутри контейнера докеров. Иногда тесты проходили без проблем, но иногда терпели неудачу. Я подумал, что это какое-то состояние гонки, но не мог понять почему. Я понял, что у меня есть afterEachшаг, который очищает базу данных (используя deleteManyметод). Добавление jest.setTimeout(30000);в beforeAllметоде , похоже, закрепился это для меня - я предполагаю , так как удаление базы данных является сетевым вызовом (в состоянии), это иногда занимает больше времени , чем на 3 секунды и метание.
nkhil

Ответы:

235

Наличие аргумента в вашей 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, когда асинхронная функция завершена. Если вы никогда не позвоните, Жасмин никогда не узнает, что ваш тест завершен, и в конечном итоге у него истечет время ожидания.

маста
источник
3
То же самое верно и для args в описании (в angular для этого нужно вызвать
inject in description
@MartinBliss Это задокументировано, я только что предложил правку для ссылки на документацию: stackoverflow.com/suggested-edits/2434606
Винсент
43
Обратите внимание на случайных гуглеров, которые столкнутся с этим вопросом в будущем: если вы используете Protractor и столкнетесь с этой проблемой, этот ответ - не то, что вы ищете - Protractor сам вызывает обратный вызов.
Винсент
Это устранило мою проблему, и это было вызвано тем же cuplrit «copy / pasta»
Shaikh
1
@Vincent в чем проблема пользователей Protractor, если возникает эта ошибка?
Bruno Bieri
60

Даже для асинхронных тестов в этих случаях срабатывает тайм-аут. Вы можете обойти эту ошибку, увеличив значение лимита тайм-аута для оценки асинхронного обратного вызова 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

Gsalgadotoledo
источник
1
Это кажется не «правильным» способом, но после добавления пары дополнительных нулей для запуска моего теста Selenium это был необходимый хак.
наждачная
Исходный jasmine.DEFAULT_TIMEOUT_INTERVAL равен 60000 мс. Таким образом, этот пример фактически сделает его в шесть раз короче.
Waltari
Вы правы, я просто ввел случайное число в этот пример, спасибо :)
gsalgadotoledo
20

Эта ошибка также может быть вызвана отсутствием инъекции при инициализации службы / фабрики или чего-то еще. Например, его можно бросить так:

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

Чтобы исправить это, просто оберните функцию с помощью inject, чтобы правильно получить службу:

var service;
beforeEach(inject(function(_TestService_) {
    service = _TestService_;
}));
Катана24
источник
14
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();
        }));
    });
Lijo
источник
7

Вы можете использовать плагин karma-jasmine для глобальной установки интервала тайм-аута по умолчанию.

Добавьте этот конфиг в karma.conf.js

module.exports = function(config) {
  config.set({
    client: {
      jasmine: {
        timeoutInterval: 10000
      }
    }
  })
}
code.rookie
источник
5

Эта ошибка началась для меня совершенно неожиданно, на тесте, который всегда работал. Я не мог найти никаких предложений, которые помогли бы, пока не заметил, что мой Macbook работает медленно. Я заметил, что процессор был привязан к другому процессу, который я убил. Ошибка асинхронности Jasmine исчезла, и мои тесты снова в порядке.

Не спрашивайте меня почему, я не знаю. Но в моих обстоятельствах, казалось, виновата нехватка системных ресурсов.

Эрик Сойк
источник
5
Вероятно, когда ваш процессор был свободен, задача завершилась до тайм-аута по умолчанию. Когда ЦП был занят, задача, которую вы тестировали, выполнялась слишком долго.
Шейн
5

Это скорее наблюдение, чем ответ, но оно может помочь другим, которые были так же разочарованы, как и я.

Я продолжал получать эту ошибку из двух тестов в моем наборе. Я думал, что просто сломал тесты с помощью рефакторинга, который я делал, поэтому после того, как откат изменений не сработал, я дважды (две версии назад) вернулся к более раннему коду, думая, что это избавит от ошибки. Это ничего не изменило. Я гнался за своим хвостом вчера весь день и часть утра, не решив проблему.

Я расстроился и проверил код на ноутбуке сегодня утром. Прогнал весь набор тестов (около 180 тестов), ошибок нет. Так что ошибок никогда не было в коде или тестах. Вернулся в свой ящик разработчика и перезагрузил его, чтобы очистить все в памяти, что могло вызвать проблему. Без изменений, одинаковые ошибки в тех же двух тестах. Поэтому я удалил каталог со своей машины и снова проверил его. Вуаля! Ошибок нет.

Не знаю, что это вызвало или как это исправить, но удаление рабочего каталога и его возврат исправили, что бы это ни было.

Надеюсь, это кому-то поможет.

Деллиотт
источник
1
Спасибо, чувак, я сходил с ума по этому поводу. Я перезагрузил свой компьютер, и все
yngrdyn
В моем случае я просто повторно запустил команду, и это устранило эту проблему. У меня была горячая перезагрузка юнит-тестов, и каждый раз она давала сбой. Мне пришлось остановиться и снова запустить команду.
jignesh
3

Вы также получаете эту ошибку, когда ожидаете чего-то в beforeAllфункции!

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

    beforeAll(function () {
        ...

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

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

    }
}
Том Ван Россом
источник
3

Не используйте done, просто оставьте вызов функции пустым.

Анастасия Семёнова
источник
Пожалуйста, поправьте меня, если я ошибаюсь, но, как я понял, это просто завершает набор тестов до самого теста, и вместо этого выводится сообщение об ошибке. Это означает, что любое неудачное утверждение не приведет к нарушению теста, поскольку набор тестов завершается до запуска утверждения. Это также может означать (я видел подобное поведение), что другой тест показывает ошибку, созданную этим тестом. Наконец, это означает, что с самого начала все выглядит нормально, но по мере роста количества тестов проблема будет периодически появляться.
LosManos,
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} );
    });
});
Paul Sweatte
источник
0

Как отметил @mastablasta, но также чтобы добавить, что если вы вызываете аргумент 'done' или, скорее, называете его завершенным, вы просто вызываете обратный вызов completed () в своем тесте, когда он будет выполнен.

// 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(){
  //...
});
SoEzPz
источник
0

жасмин.DEFAULT_TIMEOUT_INTERVAL = 100000;

Сохранение этого в блоке решило мою проблему.

it('', () => {
 jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Саи Прасад
источник
0

Что я сделал: добавил / обновил следующий код:

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
Zeeshan
источник
4
Пожалуйста, объясните, почему этот код работает, а не просто размещайте его без объяснения причин.
Коби
Таким образом, в основном, когда вы выполняете тест, и он занимает больше времени, чем ожидалось, он терпит неудачу, потому что тайм-аут по умолчанию был соблюден, и скрипт не продвинулся вперед в выполнении. Это могло произойти из-за невыполнения некоторых условий (например, видимость, загрузка страницы). Теперь, если ваш тайм-аут по умолчанию равен 1000 мс >>, сценарии будут часто давать сбой, потому что это всего одна секунда, и может быть несколько факторов, добавляющих к сбою вашего сценария. Однако увеличение интервала тайм-аута может заставить браузер / драйвер дольше ждать выполнения условий.
Zeeshan 09
2
Хорошо, теперь напишите об этом в свой пост; старайтесь не отвечать просто кодом без объяснения :)
Коби
0

Вместо того

beforeEach(() => {..

использовать

beforeEach(fakeAsync(() => {..
Мегнатх Дас
источник
0

Похоже, что тест ждет некоего обратного вызова, которого так и не произошло. Вероятно, потому что тест не выполняется с асинхронным поведением.

Во-первых, посмотрите, не используется ли просто fakeAsync в вашем «его» сценарии:

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

Вы также можете использовать flush()для ожидания завершения очереди микрозадач или tick()для ожидания определенного количества времени.

Оборотень
источник
-2

Если у вас есть аргумент ( done) в itфункции, попробуйте удалить его, а также его вызов внутри самой функции:

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        
});
tiagor87
источник
4
Без каких-либо объяснений, почему этот ответ не так полезен.
Гэри
2
этот ответ решил мою проблему ... angular тестирование - кошмар!
Benjamin Caure