簡體   English   中英

關於測試 Angular $localize (Angular 9) 的建議

[英]Advice on testing Angular $localize (Angular 9)

示例回購

可以在這里找到 repo。

請注意,這不是一個完整的示例,它只是為了展示有關測試$localize問題。

有2個分支:1. master 1. enable-localize-unit-tests -這改變了test.tspolyfills.ts防止@angular/localize/init從被導入,則$localize全局函數也被窺探

概述

我已經將一個項目從 Angular 8 升級到 9(遵循Angular 更新指南),用 Angular 的新$localize函數替換了I18n服務(來自 ngx-translation/i18n-polyfill)的任何使用(關於此的文檔非常有限, here是我能找到的最好的)。 我可以運行本地化構建並再次在特定語言環境中提供應用程序。 但是,在單元測試方面,我遇到了一些障礙。

以前,在使用i18n-polyfill ,可以將I18n服務注入到組件等中,如下所示(參見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的使用,因為它是全局函數而不是可注入服務。

為了完整I18nLocalizeComponent使用$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的間諜替換全局$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);
  });
});

間諜確實可以工作,但如果組件或其他組件在其模板中使用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)

有沒有人對如何處理這個用例有任何建議,或者目前不支持? 我還沒有看到任何對此有幫助的文檔、教程或文章,因此我們將不勝感激。

要在示例 repo 中重新創建上述問題,您必須:

  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來執行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

我發現的解決方案是監視$localizetranslate函數而不是$localize本身: test.ts

const _global: any = typeof global !== "undefined" && global;
const defaultFakedLocalizeTranslate: (messageParts: TemplateStringsArray,
                                      substitutions: readonly any[]) => [TemplateStringsArray, readonly any[]] =
  (messageParts: TemplateStringsArray, substitutions: readonly any[]) => [messageParts, substitutions];

_global.mockLocalize = createSpy("mockLocalize") as Spy;

declare global {
  const mockLocalize: Spy;
}

$localize.translate = mockLocalize.and.callFake(defaultFakedLocalizeTranslate);

確保您已在test.ts導入@angular/localize/init

I18nLocalizeComponent單元測試可以更新如下:

describe('I18nLocalizeComponent', () => {
  let component: I18nLocalizeComponent;
  let fixture: ComponentFixture<I18nLocalizeComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ I18nLocalizeComponent ],
    })
      .compileComponents();
  }));

  beforeEach(() => {
    mockLocalize.calls.reset();

    fixture = TestBed.createComponent(I18nLocalizeComponent);
    component = fixture.componentInstance;
  });

  it('should call $localize once', () => {
    expect(mockLocalize).toHaveBeenCalledTimes(1);
  });
});

此更改還將允許模板中帶有i18n標記的組件的單元測試也成功運行。

要查看更改,請參閱示例存儲庫的fix-localize-unit-tests分支。

找不到名稱“全球”

對於那些嘗試在 Angular 12 上運行它的人。

我有以下問題在此處輸入圖片說明

為了使演示運行,我必須添加以下內容
-> tsconfig.spec.json 將“節點”添加到編譯器選項。

###tsconfig.spec.json

"compilerOptions": {
types": [
"jasmine",
"node"       <====
]} 

這個答案是擴展原始帖子中的測試。

對於任何想要測試是否
$localize 翻譯正確的字符串?

這是測試的樣子:

it('should call $localize.translate with "string to translate"', () => {
    expect(mockLocalize.calls.argsFor(0)[0][0]).toContain('string to translate');
  });

這是一個回購
回購是這篇文章中給出的回購的“分叉”。

差異:

  • 更新 Angular 12
  • 添加了對翻譯字符串的測試

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM