Slide 1

Slide 1 text

Angularで簡単な画面を 単体テストまで一通り作った件 Angular もくもく会 in Fukuoka #9 2019.9.11

Slide 2

Slide 2 text

自己紹介 福岡のエンジニアです。 客先常駐するタイプです。 COBOL → VB6.0 → VB.net → Spring BootなIT人生。 他 jQuery → AngularJS → Angular 定年後の夢は、年金をもらいながら自称IT発明家になることです。 ふるてつ@tetsufuru19681 https://tetsufuru.hatenablog.com/ ちょくちょく参加するコミュニティ

Slide 3

Slide 3 text

今日のおはなし Angular Materialを使って簡単なマスタメンテ画面を作りました。 会社マスタメンテ画面とサービス 軽く動いたところで初テストコードを書きました。 Jasmineでサービス1つ、コンポーネント1つ 目新しいことは特にありませんが、せっかくなのでご紹介します。 ※HTML、CSSはまだあまりちゃんとしていません。

Slide 4

Slide 4 text

すこし実物を見ていただきます。

Slide 5

Slide 5 text

CompanyListComponent 検索条件:会社名、会社名カナ、削除フラグ ボタン:新規、クリア、検索 一覧:ページネーションあり、一覧をクリックすると詳細へジャンプ DIしているもの CompanyService(自前) FormBuilder Title Router

Slide 6

Slide 6 text

CompanyService メソッド getCompanyList(httpParams: HttpParams) getCompany(companySeq: number) createCompany(companyDto: CompanyDto) updateCompany(companyDto: CompanyDto) DIしているもの HttpClient TranslateService ErrorMessageService(自前)

Slide 7

Slide 7 text

company.service.spec.ts(デフォルト) import { TestBed, inject } from '@angular/core/testing'; import { CompanyService } from '../company/company.service'; xdescribe('CompanyService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [CompanyService] }); }); it('should be created', inject([CompanyService], (service: CompanyService) => { expect(service).toBeTruthy(); })); });

Slide 8

Slide 8 text

company.service.spec.ts describe('CompanyService', () => { let companyService: CompanyService; let httpClientSpy: { get: jasmine.Spy, post: jasmine.Spy, put: jasmine.Spy }; let translateServiceSpy: { instant: jasmine.Spy }; beforeEach(() => { httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put']); translateServiceSpy = jasmine.createSpyObj('TranslateService', ['instant']); TestBed.configureTestingModule({ providers: [ CompanyService, ErrorMessageService, { provide: HttpClient, useValue: httpClientSpy }, { provide: TranslateService, useValue: translateServiceSpy },], }); companyService = TestBed.get(CompanyService); }); 追加 追加 追加

Slide 9

Slide 9 text

company.service.spec.ts it('#getCompanyList:should return expected SearchCompanyListDto (HttpClient called once)', () => { const expectedSearchCompanyListDto: SearchCompanyListDto = new SearchCompanyListDto(); const searchCompanyDto: SearchCompanyDto[] = [{companySeq: BigInt('1'), companyName: 'companyName', ~ 中略 ~ updateUser: 'updateUser', updateTime: new Date}]; expectedSearchCompanyListDto.searchCompanyDtos = searchCompanyDto; httpClientSpy.get.and.returnValue(asyncData(expectedSearchCompanyListDto)); companyService.getCompanyList(null).subscribe( searchCompanyList => expect(searchCompanyList).toEqual(expectedSearchCompanyListDto, 'expected searchCompanyDto'),fail); expect(httpClientSpy.get.calls.count()).toBe(1, 'one call'); }); DTO SPY

Slide 10

Slide 10 text

company-list.component.spec.ts TestBed.configureTestingModule({ declarations: [CompanyListComponent], schemas: [NO_ERRORS_SCHEMA], imports: [ReactiveFormsModule, BrowserAnimationsModule, MaterialModule, HttpClientModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient] } }), ], providers: [ FormBuilder, ~ 以下略 ~ 追加 追加

Slide 11

Slide 11 text

company-list.component.spec.ts it('should entry company name', () => { const debugElement: DebugElement = fixture.debugElement; const queriedElement = debugElement.query(By.css('#companyName')); const htmlInputElement: HTMLInputElement = queriedElement.nativeElement; const expectedEntry = 'abcd1234日本語'; htmlInputElement.value = expectedEntry; htmlInputElement.dispatchEvent(new Event('input')); expect(component.companyName.value).toEqual(expectedEntry); }); css selector event

Slide 12

Slide 12 text

テストした内容 company.service 4つのメソッド(getListXX、getXX、updateXX、createXX)×2(正常/エラー2通り) company-list.component TypeScript 「新規」「クリア」「検索」ボタンから呼ばれるメソッドのテスト 「結果一覧」リストをクリックした時に呼ばれるメソッドのテスト DOM 検索条件の項目名 検索条件の初期値 検索条件の入力値 検索用のhttpパラメータ値

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

課題・検討 無駄にspyを作った気がする。 [NO_ERRORS_SCHEMA]の使いどころをまだわかっていない。 DOMのテストの成果が良くわからない ⇒ DOMのカバレッジはない のか? Materialでidを追加したコードがブラウザで表示されると違うものに なる場合があった。 検索ボタンクリックのテストは async ()=>でないと動かなかった。 DOMについて一覧のヘッダーやデータ行の内容を確認すべきか? 悩んだ。

Slide 16

Slide 16 text

参考サイト  Material 環境:https://material.angular.io/guide/getting-started コントロール:https://material.angular.io/components/categories/forms 一覧関連:https://material.angular.io/components/categories/tables ページネーションのサンプル:https://material.angular.io/components/table/examples (上記URLの「Table retrieving data through HTTP」にあります)  テスト:https://angular.jp/guide/testing 上記URL内の「サービスのテスト」、「コンポーネントテストの基本」、「コンポーネントクラスのテスト」、「コンポーネント DOMのテスト」

Slide 17

Slide 17 text

わたしのブログ記事 Angular8 で Web アプリを作ろう - Jasmine - Serviceのテスト https://tetsufuru.hatenablog.com/entry/2019/08/13/130116 Angular8 で Web アプリを作ろう - Jasmine - Componentのテスト その1 https://tetsufuru.hatenablog.com/entry/2019/09/01/015709 Angular8 で Web アプリを作ろう - Jasmine - Componentのテスト その2 DOM https://tetsufuru.hatenablog.com/entry/2019/09/01/222413

Slide 18

Slide 18 text

ありがとうございました。