Прежде чем мы продолжим, мне нужно прояснить некоторую путаницу, существующую в мире Angular с объектом Location. По умолчанию доступно местоположение из @angular/common и собственное местоположение DOM. Несмотря на это, версия Angular предоставляет .go() функцию, по сути, она взаимодействует только с маршрутизатором и не перезагружает страницу, как это делает объект DOM. Итак, для реального взаимодействия с браузером вы должны использовать версию DOM, что ставит вас перед проблемой, как это проверить?

К сожалению, за ним невозможно шпионить, потому что это не доступный для записи объект.

const spy = spyOn(location, ‘assign’).and.stub();
...
Error: <spyOn> : assign is not declared writable or has no setter

Вот типичная ошибка Jasmine, если вы попытаетесь сделать это таким образом.

Внедрение зависимостей для спасения

Вместо того, чтобы напрямую манипулировать location, нам нужно внедрить наш компонент с механизмом внедрения зависимостей. Введя его, мы можем позже заменить его тестовым двойником.

Краткое введение в то, как работает DI. Если вы знакомы с ним, просто перейдите к примеру кода. Вот типичный раздел провайдеров в каком-то модуле

providers: [
  MyService,  // short way
  { provide: MyService, useClass: MyService } // full way
]

Каждое объявление поставщиков в ваших модулях фактически эквивалентно полному описанию поставщика, где MyService действует не как имя класса, а как токен для идентификации вашей службы. Этот токен Angular берет из метаданных TypeScript о ваших классах. См. Подробности в Руководстве по внедрению зависимостей.

Надеюсь, Angular предоставляет нам InjectionToken класс, который мы можем использовать для генерации любого пользовательского токена.

import { InjectionToken } from '@angular/core';
...
export const LOCATION_TOKEN = new InjectionToken<Location>('Window location object');

Чтобы использовать этот токен, мы должны использовать специальный декоратор свойств @Inject, также доступный в Angular.

@Inject(LOCATION_TOKEN) private location: Location

А теперь давайте объединим все части вместе

export const LOCATION_TOKEN = new InjectionToken<Location>('Window location object');
@Component({
  providers: [
    { provide: LOCATION_TOKEN, useValue: window.location }
  ]
})
export class SomeComponent {
  constructor(@Inject(LOCATION_TOKEN) private location: Location) {}
  useIt() {
    this.location.assign('xxx');
  }
}

Этот метод можно использовать не только для внедрения объекта Location, но и любых других сущностей, для которых нет определенных классов, таких как другие объекты WebAPI, конфигурация приложения и т. Д.