[Angular] E2E Testの自動化

E2Eテスト概要

  • Angularでは以下の二種のテストをngコマンドで実行可能(Protoractor/Karmaというテストを実行するためのフレームワークが標準で準備されている)
    • E2Eテスト
      • End to End(エンドツーエンド)テストの略
      • WEBブラウザー上でのテストを自動化できる
      • 画面毎のテスト
      • テストフレームワーク
        • Protoractor
      • コマンド
        • ng e2e
    • ユニットテスト
      • 関数ごとのテスト
      • テストフレームワーク
        • Karma
      • コマンド
        • ng test

今回は画面毎のテストを自動化したいので、Protoractorを活用してE2Eテストを実践する

  • E2Eテストまでの流れ
      1. 操作したい要素に識別子を付与
      • 要素(Ex. ボタン、入力フォーム)を判別可能にする
      1. テストを定義
      • テストコードを書く
    • マルブラウザ対応の設定をする
    • テストを実行

選択する要素に識別子を付与

E2EテストではAPの画面を自動操作する。
その際にどこを操作するか?(特定のボタン、フォームなど)を判別するために、各Componentのhtmlファイルで各要素に識別子を付与する必要がある。

  • 選択する要素を識別する識別子

    • data属性を使う
    • 書式
      • 以下を操作したい要素に定義していく
        1
        [attr.data-test]="'任意の識別子'"
      • ※要素を識別するために他に使用される属性として、id属性・class属性などもあるが、これらのはデザインの変更などで変更される恐れがあるため勧められていない。
  • 一般的なログイン画面で以下の要素に識別子を与えた例

    • ログインIDの入力欄
    • パスワードの入力欄
    • リクエストを送信するボタン
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h1>login</h1>
<form>
<div>
<mat-form-field>
<input matInput placeholder="email" [attr.data-test]="'email'"> // ← 要素①: data-*属性で一意となる識別子を振る
</mat-form-field>
</div>
<div>
<mat-form-field>
<input type="password" matInput placeholder="passoword" [attr.data-test]="'password'"> // ← 要素②
</mat-form-field>
</div>
</form>
<div class="p-button-wrapper">
<button mat-raised-button class="p-button" color="accent" (click)="login()" [attr.data-test]="'login-button'">Login</button>  // ← 要素③
</div>

この後の工程で、これらの識別子に対するアクションを定義する。
上記の例であれば、IDとパスの識別子に対して入力値を与えて、ボタンの識別子に対して(click)アクションを実行させることになる。

## テストファイル

  • テストを定義するファイルは以下の二種類
    • \angular-pj\e2e\src\*.e2e-spec.ts
    • \angular-pj\e2e\src*.po.ts

Angular CLIでプロジェクトを始めると、上記のファイルが自動生成されているはず。

Page Object

  • アプリケーションの画面単位で1オブジェクトを定義するPage Objects としてE2Eテストが作成される
  • PageObjects
    • アプリの画面を1つのオブジェクトとして捉えるデザインパターン
  • PageObjectのメリット
    • もし、テストケースに直接セレクタを記述する場合、デザイン変更がにより、セレクタを記述している全テストケースの修正が必要
    • Page Objectsとして、.po.tsに各画面毎の要素選択などの処理を定義することで、.e2e-spec.tsは実際のテストケースを手続き的に書いていける
      • コードの可読性が向上
      • 再利用性が高まる

E2Eテストを実行

テストコードのより詳細な記述方法については別途まとめます

参考

関連記事

その他

[Angular] ng e2e Test 証明書エラーでハマった際の回避策(unable to get local issuer certificate))

AnglarのE2Eテストを会社の開発環境で試みたところ、証明書エラーでハマってしまった際のメモ

Error詳細

  • E2Eテストの実行時に以下の様なエラーが出る

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    \angular-pj> ng e2e
    [21:20:51] I/config_source - curl -oD:\angular-pj\node_modules\protractor\node_modules\webdriver-manager\selenium\chrome-response.xml https://chromedriver.storage.googleapis.com/
    events.js:174
    throw er; // Unhandled 'error' event
    ^

    Error: unable to get local issuer certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
    at TLSSocket.emit (events.js:198:13)
    at TLSSocket._finishInit (_tls_wrap.js:636:8)
    Emitted 'error' event at:
    at Request.onRequestError (D:\angular-pj\node_modules\request\request.js:877:8)
    at ClientRequest.emit (events.js:198:13)
    at TLSSocket.socketErrorListener (_http_client.js:392:9)
    at TLSSocket.emit (events.js:198:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
  • webdrive-managerのアップデートにより解決した事例を真似てupdateを試みたものの、同様のエラーで進めない状況

    • Error: unable to get local issuer certificate
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      PS D:\angular-pj>  webdriver-manager update --version.chrome=86.0.4240.75
      webdriver-manager: using global installed version 12.1.7
      [21:24:30] I/config_source - curl -oD:\Users\XXXXXXXXX\AppData\Roaming\npm\node_modules\webdriver-manager\selenium\standalone-response.xml https://selenium-release.storage.googleapis.com/
      [21:24:30] I/config_source - curl -oD:\Users\XXXXXXXXX\AppData\Roaming\npm\node_modules\webdriver-manager\selenium\chrome-response.xml https://chromedriver.storage.googleapis.com/
      [21:24:30] I/config_source - curl -oD:\Users\XXXXXXXXX\AppData\Roaming\npm\node_modules\webdriver-manager\selenium\gecko-response.json https://api.github.com/repos/mozilla/geckodriver/releases
      events.js:174
      throw er; // Unhandled 'error' event
      ^

      Error: unable to get local issuer certificate
      at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
      at TLSSocket.emit (events.js:198:13)
      at TLSSocket._finishInit (_tls_wrap.js:636:8)
      Emitted 'error' event at:
      at Request.onRequestError (D:\Users\XXXXXXXXX\AppData\Roaming\npm\node_modules\webdriver-manager\node_modules\request\request.js:877:8)
      at ClientRequest.emit (events.js:198:13)
      at TLSSocket.socketErrorListener (_http_client.js:392:9)
      at TLSSocket.emit (events.js:198:13)
      at emitErrorNT (internal/streams/destroy.js:91:8)
      at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
      at process._tickCallback (internal/process/next_tick.js:63:19)

Protoractor/ng e2eについて基礎知識

  • Protoractorは内部的にSelenium WebDriverというライブラリを利用しており、入力やボタン操作などの操作の仕組みを標準で備えている

  • Protoractor(Webdriver)は単体では直接ブラウザーを操作できない

    • 内部的には、中計サーバーであるSelenium Serverを介して対象にアクセスする
      protoractor
  • directConnect

    • 有効化した場合、ChromeとFirefoxはSeleniumServerを立ち上げずにアクセスできる
  • angular-pj\node_modules\selenium-webdriver>

    • pj配下にseleniumは元から入っているように見受けられる

証明書エラー解決策: 証明書検証の回避

  • オプションにより証明書の検証を回避できる
    • –ignore_ssl
      1
      2
      angular-pj> .\node_modules\.bin\webdriver-manager update --proxy=XXXX --ignore_ssl
      angular-pj> ng e2e --no-webdriver-update

環境情報

  • package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
"name": "XXXXXXXXX",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.2.13",
"@angular/cdk": "~8.2.3",
"@angular/common": "~8.2.13",
"@angular/compiler": "~8.2.13",
"@angular/core": "~8.2.13",
"@angular/forms": "~8.2.13",
"@angular/material": "^8.2.3",
"@angular/platform-browser": "~8.2.13",
"@angular/platform-browser-dynamic": "~8.2.13",
"@angular/router": "~8.2.13",
"hammerjs": "^2.0.8",
"jquery": "^3.4.1",
"ng-material-treetable": "^0.5.5",
"rxjs": "~6.4.0",
"tslib": "^1.14.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.803.29",
"@angular/cli": "^8.3.29",
"@angular/compiler-cli": "~8.2.13",
"@angular/language-service": "~8.2.13",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^8.10.64",
"aws-sdk": "^2.768.0",
"codelyzer": "^5.2.2",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"protractor": "^5.4.4",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ng --version
> ng --version

_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/


Angular CLI: 8.3.29
Node: 10.16.3
OS: win32 x64
Angular: 8.2.14
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.803.29
@angular-devkit/build-angular 0.803.29
@angular-devkit/build-optimizer 0.803.29
@angular-devkit/build-webpack 0.803.29
@angular-devkit/core 8.3.29
@angular-devkit/schematics 8.3.29
@angular/cdk 8.2.3
@angular/cli 8.3.29
@angular/material 8.2.3
@ngtools/webpack 8.3.29
@schematics/angular 8.3.29
@schematics/update 0.803.29
rxjs 6.4.0
typescript 3.5.3
webpack 4.39.2
npm
npm -v
6.9.0

その他

参考

関連記事

その他

@EventEmitter @Input @Output @ViewChild ACM AMP API Gateway AR AR.js AR.js Studio AWS AWS Amplify AWS Budgets AWS Cost Explorer AWS SDK for JavaScript AddThis Adobe XD Alexa Amazon CloudWatch Amazon Honycode Amazon SNS Android Angular Angular Material AngularFire AppSync Augury C CDN CI/CD Cloud Craft Cloud9 CloudFormation CloudFront CloudTrail Cognito Cost Anomaly Detection Cubase ai Cubasis LE DTM Disqus DynamoDB Elgato HD 60S Firebase Firebase Hosting Former2 Github Github Actions Github.com GithubEnterprise GithubPages Google Chrome Google Cloud Shell GraphQL Hexo Hosting IAM Ionic JSON JavaScript LadioCast Logging LowCode MFA MS Authentication MacBook Pro 16 Mind Node NETDUETTO Netflix Party Netlify Network NoCode Observable PO PdM Promise RPA ReactiveForm Recognition Route53 S3 SAM(Serverless Application Model) SAR SSL SYNCROOM Schematics ScrumInc Serverless Service Siri Sitemap Spark AR Steinberg UR44C Teams Touch Cast Studio Treetable TypeScript UI UI Bakery WAF WAFv2 WEB Page Dev WEB会議 WebAR WebSocketAPI Webhook Windows Power Automate Wireshark aot async/await aws config cloud9 display.land draw.io e2e test filter() forkJoin() google search console hexo-generator-amp iOS iPad Pro iPhone icarus map() mat input mat tree mat-checkbox mat-input mat-selection-list mmhmm ngFor plantUML popIn Aladdin2 then() vscode ”global is not defined” らくがきAR アジャイル アジャイル開発 クロスプラットフォーム ショートカット スクラム スクラム開発 テレワーク ファイル操作 ブラウザ型IDE プロダクトオーナー プロダクトマネージャー プロトタイピング リモートセッション 共同開発 双方向データバインディング 待ち合わせ処理 認定スクラムマスター 静的WEB Hosting 静的WEBサイトHosting
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×