Можно сколько угодно считать себя магистром юнит-тестирования, а потом сесть и потратить чуть ли не пять часов на решение одной маленькой проблемы. Поэтому, чтобы не так жаль было потраченного времени, делюсь с вами свежедобытым инсайтом.
Вдогонку к недавнему выпуску про тестирование асинхронного кода, хочется добавить, что нужно быть очень внимательным разработчиком, когда какая-либо из ваших функций возвращает Observable<void>.
Иногда это сам запрос к серверу не шлет в ответ никакие данные, иногда мы делаем это намеренно, вручную возвращая из какого-то метода оператор of().
Тут и кроется одна неприятная особенность.
Неважно в рабочем ли коде или в тестах, когда мы возвращаем результат of(), то внезапно получаем зависший намертво тест. Несмотря на то что коллбэк done() вызывается в правильном месте.
Например, вот такой фрагмент из юнит-теста:
fakeDependency.registerShipment.and.returnValue(of()); service.registerShipment().subscribe(() => { expect(clearSpy).toHaveBeenCalled(); done(); });
Здесь метод registerShipment, изнутри возвращает метод зависимости fakeDependency.registerShipment с возвращаемым типом Observable<void>.
И этот тест повиснет на выполнении, а в конечном итоге отвалится с ошибкой таймаута. Потому что observable ничего не пришлет и соответственно не завершится.
А все потому, что оператор of() делает следующее:
Emit variable amount of values in a sequence and then emits a complete notification.
документация RxJS
То есть, оператор отправляет переменное количество значений, а затем завершается.
Но мы не передаем ничего внутри of(), соответственно, никаких значений не отправляется, Observable не завершается, и подписка на него в тесте зависает.
Поправить все это очень легко, достаточно указать undefined в скобках of() и проблема решена:
fakeDependency.registerShipment.and.returnValue(of(undefined)); service.registerShipment().subscribe(() => { expect(clearSpy).toHaveBeenCalled(); done(); });
Будте аккуратны с возвращаемыми вручную значениями Observable и обязательно пишите юнит-тесты на этот код.
На этом все. А теперь пойдемте переписывать все пустые операторы of() там, где должен был быть Observable<void>.