Angular: есть ли способ имитировать значение PLATFORM_ID в модульном тесте?

Я использую Angular Universal. У меня есть охранник для маршрута, который ведет себя по-разному в зависимости от того, работаю ли я на сервере или на платформе браузера. Вот охранник:

export class UniversalShellGuard implements CanActivate {
  private isBrowser: boolean;

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    console.info('PLATFORM_ID = ' + platformId);
    this.isBrowser = isPlatformBrowser(this.platformId);
  }

  canActivate(): Observable<boolean> | Promise<boolean> | boolean {
    return !this.isBrowser;
  }
}

Как видите, охранник вводит PLATFORM_ID и использует его, чтобы определить, является он canActivate() или нет.

Теперь я хотел написать простой модульный тест для сторожа и сделал следующее:

describe('UniversalShellGuard', () => {
  let guard: UniversalShellGuard;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        UniversalShellGuard,
        // Idea: for now I just want to test the behaviour if I would be on the browser, so I would just use a fixed value for PLATFORM_ID
        { provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID },
      ],
    });

    guard = TestBed.get(UniversalShellGuard);
  });

  it('should deny', () => {
    expect(guard.canActivate()).toBe(false);
  });
});

Но это дает следующую ошибку:

ERROR in ./src/app/universal-shell.guard.spec.ts
Module not found: Error: Can't resolve '@angular/common/src/platform_id' in '/my-app-path/src/app'
 @ ./src/app/universal-shell.guard.spec.ts 4:0-70 11:50-69
 @ ./src sync \.spec\.ts$
 @ ./src/test.ts

Я даже попробовал простую и прямолинейную конструкцию ограждения, не используя угловой TestBed:

it('should deny', () => {
  const guard = new UniversalShellGuard(PLATFORM_BROWSER_ID);
  expect(guard.canActivate()).toBe(false);
});   

Та же ошибка.

Есть ли способ предоставить фиксированное значение для PLATFORM_ID для правильного модульного тестирования такой защиты?

Тестирование функциональных 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
7
0
2 707
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Значение PLATFORM_BROWSER_ID не является частью общедоступного API, поэтому оно импортирует его из глубокого пути, это не разрешено. Вместо этого вы можете просто поставить 'browser':

{ provide: PLATFORM_ID, useValue: 'browser' },

Для любой другой платформы вы можете использовать значения эти:

export const PLATFORM_BROWSER_ID = 'browser';
export const PLATFORM_SERVER_ID = 'server';
export const PLATFORM_WORKER_APP_ID = 'browserWorkerApp';
export const PLATFORM_WORKER_UI_ID = 'browserWorkerUi';

Это глупо, но работает ... Метод isPlatformBrowser должен получать объект, но насмешка со строкой - это нормально.

C0ZEN 02.10.2019 15:45

Хороший. Я также столкнулся с этой проблемой в тесте модуля защиты, и в итоге мой код configureTestingModule был заключен в функцию configure(), которая принимает идентификатор платформы в качестве параметра и использует его в provide. Функция configure() вызывается кодом beforeEach в блоке describe с тестом SSR и другим блоком describe с обычными тестами браузера.

Jon Onstott 04.01.2021 21:31

Другой способ провести тест - это имитировать результат isPlatformBrowser. Для этого вы должны создать оболочку этой функции:

export class UniversalShellGuard implements CanActivate {
    ...
    private isBrowser(): boolean {
        return isPlatformBrowser(this.platformId);
    }
}

А затем, используя жасмин шпион, вы можете имитировать возвращаемое значение вашего метода isBrowser():

describe('UniversalShellGuard', () => {
    let guard: UniversalShellGuard;

    beforeEach(() => {
      TestBed.configureTestingModule({
        providers: [
          UniversalShellGuard,
          { provide: PLATFORM_ID, useValue: '' },
        ],
      });

      guard = TestBed.get(UniversalShellGuard);
    });

    it('should deny', () => {
      spyOn(guard, 'isBrowser').and.returnValue(true);
      expect(guard.canActivate()).toBe(false);
    });
});

isBrowser - это частный метод, вы получите ошибку в коде TS, потому что вы не можете получить доступ к частным методам и шпионить за ними.

Виталик Бойко 22.04.2021 18:28

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