Модульное тестирование эффекта NgRx, чтобы убедиться, что сервисный метод был вызван - не работает - PullRequest
0 голосов
/ 21 января 2019

Я использую версию NgRx ^ 7.0.0. Это мой класс эффекта NgRx:

import { Injectable } from '@angular/core';
import { ApisService } from '../apis.service';
import { Effect, Actions, ofType } from '@ngrx/effects';
import { Observable } from 'rxjs';
import { ApisActionTypes, ApisFetched } from './apis.actions';
import { mergeMap, map } from 'rxjs/operators';

@Injectable()
export class ApisEffects {

  constructor(private apisS: ApisService, private actions$: Actions) { }

  @Effect()
  $fetchApisPaths: Observable<any> = this.actions$.pipe(
    ofType(ApisActionTypes.FetchApisPaths),
    mergeMap(() =>
      this.apisS.fetchHardCodedAPIPaths().pipe(
        map(res => new ApisFetched(res))
      )
    )
  );
}

И это простой тест. Как вы видите, он должен потерпеть неудачу, но всегда проходит. Я следовал за подобным вопросом здесь в StackOverflow Как выполнить модульное тестирование этого эффекта (с {dispatch: false})? , но это не работает для меня, как будто выполнение кода никогда не входит в эффекты. $ FetchApisPaths. блок подписки

import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { hot, cold } from 'jasmine-marbles';
import { Observable, ReplaySubject } from 'rxjs';
import { ApisEffects } from '../state/apis.effects';
import { ApisFetch, ApisFetched } from '../state/apis.actions';
import { IApiPath } from '../models';
import { convertPaths, getAPIPathsAsJson, ApisService } from '../apis.service';
import { ApisServiceMock } from './mocks';

describe('Apis Effects', () => {
  let effects: ApisEffects;
  let actions: Observable<any>;
  let apisS: ApisService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        ApisEffects,
        provideMockActions(() => actions),
        {
          provide: ApisService,
          useClass: ApisServiceMock
        }
      ]
    });

    effects = TestBed.get(ApisEffects);
    apisS = TestBed.get(ApisService);
  });

  it('should call ApisService method() to get Api Paths', () => {
    const spy = spyOn(apisS, 'fetchHardCodedAPIPaths');

    const action = new ApisFetch();
    actions = hot('--a-', {a: action});

    effects.$fetchApisPaths.subscribe(() => {
      console.log('%c effect trigerred', 'color: orange; border: 1px solid red;');
      // expect(spy).toHaveBeenCalled();
      expect(true).toBe(false); // never fails
    });
  });
});

На случай, если я что-то сделаю глупо с действиями, вот файл действий: Скорее всего, нет, так как он работает в приложении, как ожидалось.

import { Action } from '@ngrx/store';
import { IApiPath } from '../models';

export enum ApisActionTypes {
    FetchApisPaths = '[Apis] Fetch Paths',
    FetchedApisPaths = '[Apis] Fetched Paths'
}

export class ApisFetch implements Action {
    readonly type = ApisActionTypes.FetchApisPaths;
}

export class ApisFetched implements Action {
    readonly type = ApisActionTypes.FetchedApisPaths;
    constructor(public payload: IApiPath[]) {}
}

export type ApisActions = ApisFetch | ApisFetched;

======================= EDIT ======================= =======

Я использовал пример из официальных документов ngrx https://ngrx.io/guide/effects/testing и теперь я могу успешно войти в блок подписки ниже, я записываю оба журнала консоли, но тест завершается успешно. Это странно! Я попытался выдать ошибки из блока подписки, и проверка по-прежнему прошла успешно.

it('should work also', () => {
    actions$ = new ReplaySubject(1);

    actions$.next(new ApisFetch());

    effects.$fetchApisPaths.subscribe(result => {
      console.log('will be logged');
      expect(true).toBe(false); // should fail but nothing happens - test succeeds
      console.log(' --------- after '); // doesn't get called, so the code
      // execution stops on expect above
    });
  });

1 Ответ

0 голосов
/ 22 января 2019

Хорошо, так что я получил это работает.Чтобы успешно проверить, вызывается ли конкретный метод службы Angular из эффекта NgRx, я поместил тестовый пример в async:

  it('should call ApisService method to fetch Api paths', async () => {
    const spy = spyOn(apisS, 'fetchHardCodedAPIPaths');

    actions$ = new ReplaySubject(1);
    actions$.next(new ApisFetch());
    await effects.$fetchApisPaths.subscribe();

    expect(spy).toHaveBeenCalled();
  });

I await effects.$fetchApisPaths.subscribe();, чтобы заблокировать выполнение и выполнить тестовое утверждениев следующей строке.

Теперь, когда я пытаюсь запустить expect(true).toBe(false);, чтобы проверить, не прошел ли тест, он не проходит должным образом.

Проблема с моим кодом в вопросе (пример с ReplaySubject как в ngrx docs https://ngrx.io/guide/effects/testing) заключалось в том, что было невозможно выполнить проверку, когда утверждение находилось внутри блока .subscribe().Там происходило что-то сомнительное, и я до сих пор не знаю точно, почему код вел себя следующим образом:

effects.$fetchApisPaths.subscribe(result => {
  console.log('will be logged');  // 1) gets logged
  expect(true).toBe(false);       // 2) should fail
  console.log(' - after ');       // 3) doesn't get called
});  

Таким образом, выполнение кода останавливается на строке 2) ,тестовый пример возвращает положительное значение, и строка 3) никогда не выполняется.

Таким образом, контрольный пример в документе ngrx с утверждением внутри блока .subscribe() всегда будет зеленым, что дает ложный положительный результат для вашегопрецедент.Такое поведение я испытал с ngrx ^7.0.0

...