MockStore в NgRx v7.0
NgRx v7.0 включает в себя выпуск нового @ngrx/store/testing
модуля, в котором есть фиктивный магазин для использования при тестировании приложений NgRx. Модуль был представлен в # 1027 с некоторой документацией, следующей за # 1591.
Примечание. Вы можете использовать этот API и функции в NgRx v5 и v6 с помощью автономного пакета ngrx-mockstore.
В настоящее время документация невелика и не содержит полного рабочего примера кода. Я приведу два примера, которые помогут прояснить ситуацию.
Существующий: StoreModule
Было возможно настроить хранилище NgRx в модульном тесте, указав StoreModule
в конфигурации тестового модуля. StoreModule создает хранилище с начальным состоянием, определенным в редукторе хранилища. Чтобы обусловить желаемое состояние для данного тестового примера, вам может потребоваться выполнить несколько действий.
Новое: MockStore
Класс MockStore
предоставляет более простой способ обусловить состояние NgRx в модульных тестах. Вы указываете начальное состояние по умолчанию, а затем обновляете состояние с помощью setState(<nextState>)
.
Давайте посмотрим, как MockStore может упростить существующий набор тестов:
Пример тестирования Auth Guard
Пример-приложение NgRx содержит AuthGuard, который предоставляет нам простой пример использования MockStore:
AuthGuard
выбирает getLoggedIn
из магазина. Если последнее getLoggedIn
истинно, отправляется действие LoginRedirect
, и функция возвращает ложь. Если последний getLoggedIn равен false, он возвращает true.
В существующем тесте AuthGuard используется StoreModule
, который требует, чтобы тест отправил действие LoginSuccess
, чтобы селектор getLoggedIn
возвратил истину:
Давайте проведем рефакторинг тех же тестов, чтобы определить состояние магазина без действий, используя MockStore
:
Вот шаги:
- Строка 6: Объявите
MockStore
, используя утверждение того же типа, которое используется при объявлении Store в AuthGuard (fromAuth.State
). - Строка 7. Создайте начальное состояние, соответствующее тому же интерфейсу состояния, которое было заявлено в строке 6. Это будет состояние по умолчанию для всех тестов. Поскольку
fromAuth.State
extends
fromRoot.State
и наши тесты зависят только отuser
атрибут, мы можем преобразовать все остальное. - Строка 19: укажите
MockStore
, используяprovideMockStore
, передавinitialState
, созданный на предыдущем шаге. - Строка 22: введите
Store
внутрь теста. - Строка 31: Чтобы задать другое состояние, используйте
setState
.
Тестирование Effect + с последним из примера
Я наткнулся на NgRx issue # 414, который описывает эффекты тестирования сложности, которые включают состояние с использованием оператора withLatestFrom
и StoreModule
.
@Effect() example$ = this.actions$.pipe( ofType(ActionTypes.ExampleAction), withLatestFrom(this.store.pipe( select(fromExample.getShouldDispatchActionOne) )), map(([action, shouldDispatchActionOne]) => { if (shouldDispatchActionOne) { return new ActionOne(); } else { return new ActionTwo(); } }) );
Внедренное состояние эффекта нельзя было изменить после вызова TestBed.get(<effect>)
, что затрудняло тестирование различных значений, выбранных getShouldDispatchActionOne
в приведенном выше фрагменте. Три распространенных обходных пути:
- Используйте
SpyOn
Жасмин, чтобы имитировать возвращаемое значениеstate.select(…)
:spyOn(store, 'select').and.returnValue(of(initialState))
. Однакоselect
теперь является оператором RxJs. ❌ - Переместите
TestBed.get(<effect>)
изbeforeEach
в каждый отдельный тест после того, как состояние будет соответствующим образом обусловлено. 😐 - Создайте mockStore (эй, разве у нас нет его сейчас?). 😀
Давайте посмотрим, как мы можем протестировать эффекты, которые используют withLatestFrom
, с помощью MockStore:
Давайте добавим новый эффект addBookSuccess$
в BookEffects
примера приложения NgRx. Когда новая книга будет успешно добавлена, мы выберем книги, которые сейчас есть в коллекции пользователя, в магазине, а затем отобразим предупреждение с другим сообщением в зависимости от количества:
Мы можем использовать MockStore
для кондиционирования состояния, что позволяет нам протестировать каждый из двух случаев:
Вот шаги, аналогичные шагам в AuthGuard
примере:
- Строка 9: объявить
MockStore
, используя утверждение того же типа, что и при объявлении Store в BookEffects (fromBooks.State
). - Строка 10. Создайте начальное состояние, соответствующее тому же интерфейсу состояния, которое было заявлено в строке 9. Это будет состояние по умолчанию для всех тестов. Поскольку
fromBooks.State
extends
fromRoot.State
и наши тесты зависят только отids
атрибут, мы можем преобразовать все остальное. - Строка 32: укажите
MockStore
, используяprovideMockStore
, передавinitialState
, созданный на предыдущем шаге. - Строка 38: введите
Store
внутрь теста. - Строка 59: Чтобы задать другое состояние, используйте
setState
.
Спасибо за прочтение! Вы можете подписаться на меня в Twitter @john_crowson :)
Чтобы узнать больше об Angular, не забудьте посмотреть последний выпуск подкаста The Angular Show.
EnterpriseNG выходит 4 и 5 ноября 2021 года.
Приходите послушать ведущих спикеров сообщества, экспертов, лидеров и команду Angular, которые в течение двух дней обсудят все, что вам нужно, чтобы максимально использовать Angular в ваших корпоративных приложениях.
Темы будут сосредоточены на следующих четырех областях:
• Monorepos
• Микро-интерфейсы
• Производительность и масштабируемость
• Удобство обслуживания и качество
Подробнее здесь ›› https://enterprise.ng-conf.org/