Функция inject
, представленная в Angular 14, даже с ограничениями — очень крутая функция. Но где свет, там и тьма. Когда мы внедряем все в конструктор класса, его легко протестировать, когда вы не используете TestBed
. После использования инъекции это может быть немного сложно.
К счастью для нас, это не невозможно. Более того, очень легко сделать код с inject
тестируемым.
Репозиторий
Как всегда, если вам нужен мой код, вы найдете его в репозитории на GitHub: github.com/galczo5/testable-inject. Чтобы упростить тестирование моего кода в ваших случаях, я опубликовал его на NPM. Вот ссылка: npmjs.com/package/testable-inject.
Проблема
Основная проблема с inject
в том, что это функция. Многие средства запуска тестов предоставляют решения для фиктивных функций и т. д. Лично мне нравится, чтобы мой код не отличался от средств выполнения тестов, поэтому я могу перенести его из Karma в Jest или, может быть, даже во что-то другое.
Чтобы решить эту проблему, мы можем просто обернуть функцию inject
. Обертка должна иметь какой-то контекст. Из соображений опыта разработчиков я сделаю его статическим.
import {isDevMode, inject as angularInject, ProviderToken} from "@angular/core"; export class Inject { private static map: Map<ProviderToken<any>, any> = new Map<ProviderToken<unknown>, unknown>(); static mock<T>(token: ProviderToken<T>, value: T) { this.map.set(token, value); } static get<T>(token: ProviderToken<T>): T { return this.map.get(token); } static has<T>(token: ProviderToken<T>): boolean { return this.map.has(token); } } export function inject<T>(token: ProviderToken<T>): T { if (isDevMode() && Inject.has<T>(token)) { return Inject.get(token); } return angularInject<T>(token); }
Inject
класс - это мой контекст здесь. С этим классом легко издеваться над чем угодно. В дополнение к контексту я добавил свою функцию inject
, которая имеет тот же API, что и исходная функция из пакета @angular/core
. Эта функция является моей оболочкой. Снаружи все, что вам нужно сделать, это изменить импорт в вашем коде, и он станет доступным для тестирования!
КСТАТИ. Он использует фиктивные значения только в режиме разработки, поэтому это не должно влиять на производственную сборку. Даже если и влияет, разница будет близка к нулю миллисекунд.
Как использовать
Допустим, у меня есть очень простой компонент для тестирования.
import {Component, ElementRef, Renderer2} from "@angular/core"; import {inject} from "testable-inject"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(elementRef: ElementRef) { const renderer = inject(Renderer2); renderer.addClass(elementRef.nativeElement, 'class'); } }
Единственное, что нужно проверить здесь, это вызов renderer.addClass
, чтобы проверить, был ли добавлен класс или нет. Обратите внимание, что ссылка inject
исходит из пакета testable-inject
, а не из @angular/core
.
Это всего лишь пример, поэтому я решил не тратить здесь время, и код модульного теста упрощен.
import {AppComponent} from "./app.component"; import {Inject} from "testable-inject"; import {Renderer2} from "@angular/core"; describe('AppComponent', () => { it('Should mock inject', () => { let itWorked = false; Inject.mock(Renderer2, { addClass() { itWorked = true; } } as unknown as Renderer2) new AppComponent({ nativeElement: null }); expect(itWorked).toBeTruthy(); }); });
Заключение
Вы можете (и должны) тестировать код с помощью inject
вызовов функций. Это просто и выглядит так же, как вызов функции Angular.
Весь код для этого опубликован в этой статье. Тот же код вы найдете в репозитории и в пакете NPM.