Как обнаружить отсутствующий tick () в fakeAsync ()

Я написал этот тест для своего приложения Angular:

it('should request confirmation before deleting & abort action if user declined', fakeAsync(() => {
    spyOn(appService, 'confirm').and.returnValue(of(false));
    spyOn(personService, 'delete').and.callThrough();
    component.deleteEntry(testPerson);
    //tick(); // Missing tick()!
    expect(personService.delete).not.toHaveBeenCalled();
}));

Это компонентный метод, который я тестирую:

async deleteEntry(person: Person) {
    if (await this.appService.confirm().toPromise()) {
        return;
    }
    try {
        await this.personService.delete(person).toPromise();
    } catch(resp) {
        this.appService.errorMsgBox();
    }
}

(Цель confirm() - показать диалоговое окно подтверждения и вернуть Observable, излучающий true / false, в зависимости от ввода пользователя)

Если вы посмотрите внимательно, в моей функции компонента есть ошибка. Я забываю !-оператор при проверке результата confirm(). Правильный код будет

    if (!await this.appService.confirm().toPromise()) {

Однако испытание пройдет. Я не уверен на 100%, но я полагаю, что он проходит, потому что оператор expect() в конце выполняет свою проверку до того, как confirm() вернет свое значение. Так что да, конечно, personService.delete() не назывался. Если я раскомментирую tick(), тест будет работать, как ожидалось, и обнаружит ошибку.

Теперь я ожидал, что fakeAsync() выдаст ошибку из-за незавершенных микрозадач. К моему удивлению, это не так. Тест проходит без ошибок и предупреждений, хотя документы говорят:

If there are any pending timers at the end of the function, an exception will be thrown.

Таким образом, похоже, что у нас есть состояние гонки, то есть confirm() разрешается до возврата fakeAsync(), но после expect(). Если это возможно, в чем дело с fakeAsync(), если не контролировать эти вещи?

Наверное, я и другие разработчики забудем tick() или flushMicrotasks() в будущем. Поэтому мне интересно, как этого избежать. Есть ли какая-то вспомогательная функция, которую мне не хватает, которую я могу добавить в afterEach()? Или поведение fakeAsync() является ошибкой Angular, т.е. должно вызывать исключение?

РЕДАКТИРОВАТЬ

См. Полный рабочий пример моей проблемы на Stackblitz: https://stackblitz.com/edit/angular-cnmubr. Обратите внимание, что вам нужно нажать кнопку «Обновить» во внутреннем представлении браузера (рядом с редактором), если вы хотите повторно запустить тест или после того, как вы что-то изменили. Функция автоматической перезагрузки не будет работать и выдает ошибки.

Я отправил проблема, как кто-то предложил в комментариях.

this.appService.confirm().toPromise() возвращает наблюдаемое или значение? Я вижу, что шпион издевается над своим returnValue как наблюдаемым т.е.of(false). AFAIK, это должно быть правдой

Oluwafemi Sule 10.09.2018 12:25
confirm() возвращает наблюдаемый объект, который излучает true, если пользователь подтверждает действие, и false в противном случае. toPromise() преобразует Observable в Promise, т.е. подписывается и разрешает возвращенное обещание с первым значением, полученным от Observable. of(false) имитирует отказ пользователя от действия (нажатие кнопки «нет» в диалоговом окне подтверждения)
fishbone 10.09.2018 12:40

Ой ну спасибо. Теперь это имеет для меня смысл. Я предполагаю, что вы можете предпочесть оболочку asyncfakeAsync и записать свои ожидания как microtask. tick вызывает ожидание выполнения обещания confirm.

Oluwafemi Sule 10.09.2018 13:28

@fishbone, не могли бы вы опубликовать репо с воспроизведением? Или разместите здесь вопрос, github.com/angular/zone.js, я проверю, спасибо.

jiali passion 10.09.2018 22:23
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
4
641
2

Ответы 2

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

Если ваши тесты, по крайней мере, написаны одинаково, то один или другой должен завершиться неудачно, если tick отсутствует / неправильный. Это не идеально, но вы можете попробовать перенести свою тестовую логику в общую функцию, чтобы иметь некоторую степень уверенности в том, что она делает то, что вы имели в виду:


function callDelete() {
    spyOn(personService, 'delete').and.callThrough();
    component.deleteEntry(testPerson);
    //tick(); // Missing tick()!
}

it('aborts delete if user declined', fakeAsync(() => {
    spyOn(appService, 'confirm').and.returnValue(of(false));
    callDelete();
    expect(personService.delete).not.toHaveBeenCalled();
}));

it('deletes if user accepted', fakeAsync(() => {
    spyOn(appService, 'confirm').and.returnValue(of(true));
    callDelete();
    expect(personService.delete).toHaveBeenCalled();
}));

Теперь ваш второй тест не удастся (потому что expect был запущен до того, как был разрешен await). Отлаживая это, вы либо заметите логическую ошибку в тестируемом коде, либо исправите ее, а затем найдете ошибку теста. Или вы найдете и исправите ошибку теста, которая приведет к сбою первого теста, а затем обнаружите логическую ошибку. Единственный способ пропустить это, если вы ошиблись с tick в одном тесте, но не в другом (или не смогли протестировать все условные переходы).

Другие вопросы по теме