Пример репо
Репо можно найти здесь .
Обратите внимание, что это не полный пример, это просто там, чтобы показать проблемы, связанные с тестированием $localize
.
Есть 2 ветви: 1. master
1. enable-localize-unit-tests
- это изменяет test.ts
и polyfills.ts
, чтобы @angular/localize/init
не был импортировано, глобальная функция $localize
также отслеживается
Обзор
Я обновил проект с Angular 8 до 9 (после Angular руководство по обновлению ), заменяющее любое использование службы I18n
(из ngx-translation / i18n-polyfill) на новую функцию Angular $localize
(документация на это довольно ограничено, здесь - лучшее, что я мог найти). Я могу запускать локализованные сборки и снова обслуживать приложение в определенной локали. Тем не менее, я столкнулся с некоторыми препятствиями, когда дело доходит до модульного тестирования.
Ранее, при использовании i18n-polyfill
, сервис I18n
мог быть внедрен в компоненты, например, c. следующим образом (см. I18nPolyfillComponent
):
@Component({
selector: "app-i18-polyfill",
template: `<h4>{{ title }}</h4>
})
export class I18nPolyfillComponent {
readonly title: string = this.i18n({
id: "title",
value: "Hello World!"
});
constructor(private i18n: I18n) {}
}
Это можно легко проверить, введя шпион в компонент:
describe("I18nPolyfillComponent", () => {
let component: I18nPolyfillComponent;
let fixture: ComponentFixture<I18nPolyfillComponent>;
let mockI18n: Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ I18nPolyfillComponent ],
providers: [
{
provide: I18n,
useValue: jasmine.createSpy("I18n"),
},
],
})
.compileComponents().then(() => {
mockI18n = TestBed.inject(I18n) as Spy;
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(I18nPolyfillComponent);
component = fixture.componentInstance;
mockI18n.and.callFake((def: I18nDef) => def.value);
});
it("should call i18n once", () => {
expect(mockI18n).toHaveBeenCalledTimes(1);
});
});
Однако я не уверен если аналогичные модульные тесты могут быть написаны для проверки использования $localize
, поскольку это глобальная функция, а не инъекционная служба.
Для полноты компонента компонент будет выглядеть следующим образом с использованием $localize
(см. I18nLocalizeComponent
):
@Component({
selector: "app-i18n-localize",
template: `<h4>{{ title }}</h4>
})
export class I18nLocalizeComponent {
readonly title: string = $localize `:@@title:Hello World!`;
}
Обоснование тестирования
Я хотел бы убедиться, что мои приложения взаимодействуют с I18n
/ $localize
надлежащим образом (вызывается правильное количество раз с правильными параметрами и т. д. c.). Это просто предотвращает глупые ошибки, если кто-то случайно меняет идентификатор транс-единицы или базовое значение перевода.
То, что я пробовал
Я пытался заменить глобальный $localize
работает со шпионом в test.ts
и избегает импорта @angular/localize/init
:
import Spy = jasmine.Spy;
import createSpy = jasmine.createSpy;
const _global: any = typeof global !== "undefined" && global;
_global.$localize = createSpy("$localize");
declare global {
const $localize: Spy;
}
И затем использует spied $localize
в тестах (см .:
describe("I18nLocalizeComponent", () => {
let component: I18nLocalizeComponent;
let fixture: ComponentFixture<I18nLocalizeComponent>;
let mockI18n: Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ I18nLocalizeComponent ],
})
.compileComponents();
}));
beforeEach(() => {
$localize.calls.reset();
$localize.and.returnValue("Hello World!);
fixture = TestBed.createComponent(I18nLocalizeComponent);
component = fixture.componentInstance;
});
it("should call $localize once", () => {
expect($localize).toHaveBeenCalledTimes(1);
});
});
Шпион работает, но тесты не пройдут, если компонент или другой компонент использует директивы i18n
в своем шаблоне, например (I18nLocalizeTemplateComponent
):
<p i18n>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat.</p>
This потерпит неудачу со следующей ошибкой:
TypeError: Cannot read property 'substr' of undefined
at <Jasmine>
at removeInnerTemplateTranslation (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:34560:1)
at getTranslationForTemplate (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:34582:1)
at i18nStartFirstPass (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:34771:1)
at ɵɵi18nStart (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:34718:1)
at ɵɵi18n (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:35450:1)
at I18nLocalizeTemplateComponent_Template (ng:///I18nLocalizeTemplateComponent.js:15:9)
at executeTemplate (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11949:1)
at renderView (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11735:1)
at renderComponent (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:13244:1)
at renderChildComponents (http://localhost:9877/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11538:1)
Error: Expected undefined to be truthy.
at <Jasmine>
at UserContext.<anonymous> (http://localhost:9877/_karma_webpack_/src/app/i18n-localize-template/i18n-localize-template.component.spec.ts:23:23)
at ZoneDelegate.invoke (http://localhost:9877/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:364:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9877/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:292:1)
Кто-нибудь есть какие-либо советы о том, как обрабатывать этот вариант использования или в настоящее время он не поддерживается? Я не видел никакой документации, учебные пособия или статьи, которые помогают с этим поэтому любая помощь будет принята с благодарностью.
Чтобы воссоздать проблему, описанную выше в репозитории, вам необходимо:
- Раскомментировать код в
test.ts
- Закомментируйте импорт из
@angular/localize/init
в polyfills.ts
- Run te sts для
I18nLocalizeTemplateComponent
Тесты могут быть выполнены с использованием npm run test -- --include src/app/i18n-localize-template/i18n-localize-template.component.spec.ts
В качестве альтернативы, используйте enable-localize-unit-tests
ответвление, затем выполните шаг 3.
Примечания
- Следующее было добавлено к
polyfills.ts
для проекта согласно Angular руководству по обновлению: import "@angular/localize/init";
- I В настоящее время я использую Карму (
4.4.1
) и Жасмин (3.5.9
) для модульного тестирования
Сведения об окружающей среде
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 9.0.6
Node: 13.2.0
OS: darwin x64
Angular: 9.0.6
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, localize, platform-browser
... platform-browser-dynamic, router
Ivy Workspace: Yes
Package Version
------------------------------------------------------------
@angular-devkit/architect 0.900.6
@angular-devkit/build-angular 0.900.6
@angular-devkit/build-ng-packagr 0.900.6
@angular-devkit/build-optimizer 0.900.6
@angular-devkit/build-webpack 0.900.6
@angular-devkit/core 9.0.6
@angular-devkit/schematics 9.0.6
@angular/cdk 9.1.3
@ngtools/webpack 9.0.6
@schematics/angular 9.0.6
@schematics/update 0.900.6
ng-packagr 9.0.3
rxjs 6.5.4
typescript 3.7.5
webpack 4.41.2