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:

Вот шаги:

  1. Строка 6: Объявите MockStore, используя утверждение того же типа, которое используется при объявлении Store в AuthGuard (fromAuth.State).
  2. Строка 7. Создайте начальное состояние, соответствующее тому же интерфейсу состояния, которое было заявлено в строке 6. Это будет состояние по умолчанию для всех тестов. Поскольку fromAuth.State extends fromRoot.State и наши тесты зависят только от user атрибут, мы можем преобразовать все остальное.
  3. Строка 19: укажите MockStore, используя provideMockStore, передав initialState, созданный на предыдущем шаге.
  4. Строка 22: введите Store внутрь теста.
  5. Строка 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 в приведенном выше фрагменте. Три распространенных обходных пути:

  1. Используйте SpyOn Жасмин, чтобы имитировать возвращаемое значение state.select(…): spyOn(store, 'select').and.returnValue(of(initialState)). Однако select теперь является оператором RxJs. ❌
  2. Переместите TestBed.get(<effect>) из beforeEach в каждый отдельный тест после того, как состояние будет соответствующим образом обусловлено. 😐
  3. Создайте mockStore (эй, разве у нас нет его сейчас?). 😀

Давайте посмотрим, как мы можем протестировать эффекты, которые используют withLatestFrom, с помощью MockStore:

Давайте добавим новый эффект addBookSuccess$ в BookEffects примера приложения NgRx. Когда новая книга будет успешно добавлена, мы выберем книги, которые сейчас есть в коллекции пользователя, в магазине, а затем отобразим предупреждение с другим сообщением в зависимости от количества:

Мы можем использовать MockStore для кондиционирования состояния, что позволяет нам протестировать каждый из двух случаев:

Вот шаги, аналогичные шагам в AuthGuard примере:

  1. Строка 9: объявить MockStore , используя утверждение того же типа, что и при объявлении Store в BookEffects (fromBooks.State).
  2. Строка 10. Создайте начальное состояние, соответствующее тому же интерфейсу состояния, которое было заявлено в строке 9. Это будет состояние по умолчанию для всех тестов. Поскольку fromBooks.State extends fromRoot.State и наши тесты зависят только от ids атрибут, мы можем преобразовать все остальное.
  3. Строка 32: укажите MockStore, используя provideMockStore, передав initialState, созданный на предыдущем шаге.
  4. Строка 38: введите Store внутрь теста.
  5. Строка 59: Чтобы задать другое состояние, используйте setState.

Спасибо за прочтение! Вы можете подписаться на меня в Twitter @john_crowson :)

Чтобы узнать больше об Angular, не забудьте посмотреть последний выпуск подкаста The Angular Show.

EnterpriseNG выходит 4 и 5 ноября 2021 года.

Приходите послушать ведущих спикеров сообщества, экспертов, лидеров и команду Angular, которые в течение двух дней обсудят все, что вам нужно, чтобы максимально использовать Angular в ваших корпоративных приложениях.
Темы будут сосредоточены на следующих четырех областях:
• Monorepos
• Микро-интерфейсы
• Производительность и масштабируемость
• Удобство обслуживания и качество
Подробнее здесь ›› https://enterprise.ng-conf.org/