angular - 关于测试 Angular $localize 的建议 (Angular 9)

标签 angular unit-testing angular-i18n

示例存储库

可以找到该存储库 here

请注意,这不是一个完整的示例,它只是为了显示有关测试 $localize 的问题。

有2个分支: 1. 大师 1. enable-localize-unit-tests - 这会更改 test.tspolyfills.ts 以防止 @angular/localize/init 被导入,$localize 全局函数也被监视

概述

我已将项目从 Angular 8 升级到 9(在 Angular update guide 之后),用 Angular 的新 $localize 函数替换 I18n 服务(来自 ngx-translation/i18n-polyfill)的任何使用(有关于此的文档非常有限,here 是我能找到的最好的文档)。我可以运行本地化构建并再次在特定区域设置中提供应用程序。然而,在单元测试方面我遇到了一些障碍。

以前,当使用 i18n-polyfill 时,I18n 服务可以注入(inject)到组件等中,如下所示(请参阅 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) {}
}

通过将 spy 注入(inject)组件可以轻松测试:

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 的使用,因为它是一个全局函数而不是可注入(inject)服务。

为了完整起见,使用 $localize 组件将如下所示(请参阅 I18nLocalizeComponent ):

@Component({
  selector: "app-i18n-localize",
  template: `<h4>{{ title }}</h4>
})
export class I18nLocalizeComponent {
  readonly title: string = $localize `:@@title:Hello World!`;
}

测试理由

我想确保我的应用程序与 I18n/$localize 进行适当的交互(调用正确的次数、使用正确的参数等)。如果有人不小心更改了跨单元 ID 或基本翻译值,这只是防止出现愚蠢的错误。

我尝试过的

我尝试用 test.ts 中的 spy 替换全局 $localize 函数,并避免导入 @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;
}

然后在测试中使用监视的 $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);
  });
});

spy 确实有效,但如果组件或其他组件在其模板中使用 i18n 指令,测试将会失败,例如 ( I18nLocalizeTemplateComponent ):

<p i18n>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat.</p>

这将失败并出现以下错误:

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)

有人对如何处理此用例有任何建议吗?或者目前不支持此用例?我还没有看到任何对此有帮助的文档、教程或文章,因此我们将不胜感激。

要在示例存储库中重新创建上述问题,您必须:

  1. 取消 test.ts 中代码的注释
  2. 注释掉 polyfills.ts 中从 @angular/localize/init 导入的内容
  3. 运行 I18nLocalizeTemplateComponent 测试

可以使用npm run test -- --include src/app/i18n-localize-template/i18n-localize-template.component.spec.ts来执行测试

或者,使用 enable-localize-unit-tests 分支,然后按照步骤 3 进行操作。

注释

  1. 根据 Angular 升级指南,以下内容已添加到项目的 polyfills.ts 中:
    import "@angular/localize/init";
    
  2. 我目前正在使用 Karma (4.4.1) 和 Jasmine (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

最佳答案

将 i18n polyfill 添加到您的测试设置中。

导入“@angular/localize/init”添加到test-setup.ts

关于angular - 关于测试 Angular $localize 的建议 (Angular 9),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60847354/

相关文章:

多个 i18n 语言环境的 angular.json 设置

d3.js - 是否有可能让 Angular2 和 D3.js 一起工作?

JavaScript 和 CSS : How to log position of a div between css translate start and end

unit-testing - 如何在 ember 集成测试中触发文件输入的更改?

c++ - gtest - 确保某个方法之前没有被调用,但可以在某个方法调用之后被调用

angular - 缺少带 Angular 区域设置 "XXX"的区域设置数据

Angular 国际化插值字符串

html - 如何随着列表的增长扩展列表的高度?

angular - 属性 'value' 在primeng 表上的类型 'FilterMetadata' 上不存在

python - 如何使用 Python unittest assertRaises 来测试该函数不会抛出任何异常?