Прежде чем мы продолжим, мне нужно прояснить некоторую путаницу, существующую в мире 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, конфигурация приложения и т. Д.