Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Angular2 rc.1 unit testing overview

1,442 views

Published on

ng-mtg#7 Angular勉強会 2016-05-17(火)
https://angularjs-jp.doorkeeper.jp/events/42684

Published in: Engineering

Angular2 rc.1 unit testing overview

  1. 1. Angular2-rc.1 Unit testing Overview 2016/05/17 mg-mtg#7 Mitsuru Ogawa(@mitsuruog)
  2. 2. Who am I? - 小川 充(@mitsuruog) - Front-end engineer@Givery - works - Angular1.4 - ユニットテスト、E2Eテスト - フロントエンドのCI
  3. 3. Today’s motivation and goal − Motivation  - Release Candidateが近いので、そろそろテストを書くノウハウが必要  - Angular1のテスト資産をどうするべきか、が心配になってきた - Goal  - Angular2でのテストについての概要を知る  - Angular1のテスト資産の戦略的生かし方
  4. 4. Table contents 1. Angular2ユニットテストの基本 1.1. ツールとセットアップ 1.2. Pipeのテスト 1.3. Serviceのテスト 1.4. Componentのテスト 2. Angular1からの変更点 3. 最近のアップデート情報 4. Angular1テスト資産の生存戦略 以前話した内容の拡大版です http://goo.gl/1SpphI
  5. 5. 1.Angular2 ユニットテストの基本
  6. 6. ツールとセットアップ - Typescript - Karma - Jasmine - SystemJS Karma SystemJS(モジュールローダー) SpecApplication Karma.conf.js => Karma-test.shim.js => Angular2ユニットテストスタック
  7. 7. Pipeのテスト import {Pipe, PipeTransform} from '@angular/core'; @Pipe({ name: 'sayHello' }) export class SayHelloPipe implements PipeTransform { transform(value:string):string { return `Hello ${value}`; } }
  8. 8. Pipeのテスト import { describe, it, expect, beforeEach } from '@angular/testing'; import { SayHelloPipe } from './say-hello.pipe'; テストで利用するモジュールをimportする
  9. 9. Pipeのテスト import { describe, it, expect, beforeEach } from '@angular/testing'; import { SayHelloPipe } from './say-hello.pipe'; describe('Test: SayHelloPipe', () => { let testee; beforeEach(() => { testee = new SayHelloPipe(); }); }); インスタンスを取得する
  10. 10. Pipeのテスト import { describe, it, expect, beforeEach } from '@angular/testing'; import { SayHelloPipe } from './say-hello.pipe'; describe('Test: SayHelloPipe', () => { let testee; beforeEach(() => { testee = new SayHelloPipe(); }); it('should say hello', () => { expect(testee.transform('world')).toEqual('Hello world'); }); }); テストを実行して結果を比較する
  11. 11. Serviceのテスト import {Injectable} from "@angular/core"; @Injectable() export class SayHelloService { constructor() {} say():string { return 'Hello'; } }
  12. 12. Serviceのテスト import {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing'; import {SayHelloService} from "./say-hello.service"; テストで利用するモジュールをimportする
  13. 13. Serviceのテスト import {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing'; import {SayHelloService} from "./say-hello.service"; describe('Test: SayHelloService', () => { beforeEachProviders(() => [ SayHelloService ]); }); 後でServiceをDIする際に利用するprovidorを指 定する。 (何もOverrideせずそのまま利用する設定)
  14. 14. Serviceのテスト import {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing'; import {SayHelloService} from "./say-hello.service"; describe('Test: SayHelloService', () => { beforeEachProviders(() => [ SayHelloService ]); it('Should say Hello', inject([SayHelloService], (testee:SayHelloService) => { })); }); injectでテストコンテキストにserviceをDIする
  15. 15. Serviceのテスト import {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing'; import {SayHelloService} from "./say-hello.service"; describe('Test: SayHelloService', () => { beforeEachProviders(() => [ SayHelloService ]); it('Should say Hello', inject([SayHelloService], (testee:SayHelloService) => { expect(testee.say()).toEqual('Hello'); })); }); テストを実行して結果を比較する
  16. 16. Serviceのテスト import {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing'; import {SayHelloService} from "./say-hello.service"; import {provide} from "@angular/core"; import {SayHelloServiceMock} from "./say-hello.service.mock"; describe('Test: SayHelloService', () => { beforeEachProviders(() => [ provide(SayHelloService, { useClass: SayHelloServiceMock }) ]); }); providorを書き換えることで、Serviceを Overrideすることもできる (SayHelloServiceMockでOverrideしている) BONUS
  17. 17. Componentのテスト import {Component, OnInit} from "@angular/core"; import {SayHelloService} from "./say-hello.service"; @Component({ selector: 'say-hello', template: '<div>Hello</div>', providers: [SayHelloService] }) export class SayHelloComponent implements OnInit { constructor(private service:SayHelloService) {} ngOnInit() { console.log(this.service.say()); } }
  18. 18. Componentのテスト① import { describe, it, inject, async, expect, beforeEachProviders } from '@angular/core/testing'; import { TestComponentBuilder, ComponentFixture } from '@angular/compiler/testing'; import { Component } from '@angular/core'; import { SayHelloComponent } from './say-hello.component'; テストで利用するモジュールをimportする
  19. 19. Componentのテスト① import { describe, it, inject, async, expect, beforeEachProviders } from '@angular/core/testing'; import { TestComponentBuilder, ComponentFixture } from '@angular/compiler/testing'; import { Component } from '@angular/core'; import { SayHelloComponent } from './say-hello.component'; @Component({ selector: 'test-container', template: '<say-hello></say-hello>', directives: [SayHelloComponent] }) class TestComponent {} テストfixtureを作成する
  20. 20. Componentのテスト② describe('Test: SayHelloComponent', () => { let builder; beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; })); }); TestComponentBuilderを初期化して Cacheしておく
  21. 21. Componentのテスト② describe('Test: SayHelloComponent', () => { let builder; beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; })); it('Should display Hello', async(() => { return builder.createAsync(TestComponent) .then((fixture: ComponentFixture<TestComponent>) => { }); })); }); TestComponentを初期化してfixtureを取得する
  22. 22. Componentのテスト② describe('Test: SayHelloComponent', () => { let builder; beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; })); it('Should display Hello', async(() => { return builder.createAsync(TestComponent) .then((fixture: ComponentFixture<TestComponent>) => { let div = fixture.nativeElement.querySelector('div'); expect(div).toHaveText('Hello'); }); })); }); fixtureからHTML Elementを取得して 結果を比較する
  23. 23. Componentのテスト② describe('Test: SayHelloComponent', () => { let builder; beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; })); it('Should display Hello', async(() => { let template = '<say-hello></say-hello>'; return builder.overrideTemplate(TestComponent, template) .createAsync(TestComponent) .then((fixture: ComponentFixture<TestComponent>) => { let div = fixture.nativeElement.querySelector('div'); expect(div).toHaveText('Hello'); }); })); }); fixtureのtemplateは別のものに切り替え可能で す BONUS
  24. 24. 2.Angular1 からの変更点 https://goo.gl/lQ4s02 詳しくはこちらへ =>
  25. 25. Angular1からの変更点 - Jasmine - describe, it, matcher, spy, mock … - Dependency injection => inject - 注入方法:_moduleName_の奇妙なルール - HttpMock => $httpbackend - TestFixtureの作成 =>$compile - 変更検知 - digestループ - scope.$digest() - モジュールのロード - module - モジュールの書き換え - $provide - Jasmine - 独自拡張したngMatcherが存在 - Dependency injection => inject - 注入方法:Typescriptによる型 - HttpMock => MockBackend - TestFixtureの作成 => TestComponentBuilder - 変更検知 - Change Detection - fixture.detectChanges() - モジュールのロード - import - モジュールの書き換え - beforeEachProviders
  26. 26. Angular1からの変更点 - Jasmine - describe, it, matcher, spy, mock … - Dependency injection => inject - 注入方法:_moduleName_の奇妙なルール - HttpMock => $httpbackend - TestFixtureの作成 =>$compile - 変更検知 - digestループ - scope.$digest() - モジュールのロード - module - モジュールの書き換え - $provide - Jasmine - 独自拡張したngMatcherが存在 - Dependency injection => inject - 注入方法:Typescriptによる型 - HttpMock => MockBackend - TestFixtureの作成 => TestComponentBuilder - 変更検知 - Change Detection - fixture.detectChanges() - モジュールのロード - import - モジュールの書き換え - beforeEachProviders 小 さ い 大 き い
  27. 27. 3.最近のアップデート (テスト周辺) https://www.ng-conf.org
  28. 28. - @angular/core/testing - describe, it, inject, etc… - @angular/compiler/testing - TestComponentBuilder - @angular/platform-browser/testing パッケージ名の変更 - angular2/testing - describe, it, inject, etc... - TestComponentBuilder - angular2/platform/testing/browser RCBeta.17
  29. 29. Test APIの改善 - 非同期処理、変更検知が格段に良くなった - [RC] injectAsync => async - [Bata.16] autoDetectChanges() の導入 - 詳しくはJulie Ralphのプレゼンをチェック - Zone.js!!Zone.js!!Zone.js!! Testing you Tasks: Julie Ralph https://youtu.be/DltUEDy7ItY
  30. 30. 4.Angular1 テスト資産の生存戦略
  31. 31. 基本方針 - 移行ではなく「移植」 - 残せるものを残す。残せる部分を増やす。 - 厳しい冬を想定したテストコードの剪定。 - Viewに依存しない「純粋なロジック」のみを移植 - ViewはComponentで書き直すケースが多くなる。 - Directiveに代表されるComponentとして再利用できるものは移植 OK。
  32. 32. ユニットテストコード移植パス filter pipe providor, service, factory service directive controller directive
  33. 33. ユニットテストコード移植パス filter pipe providor, service, factory service directive controller directive
  34. 34. Controllerテストコード生存戦略 - Controller Helper Service パターン - 純粋なロジックをController Helper Serviceに移動、これを将来移植する - 既存のテストコードは Controller Helper Service に適用しておく angular.module('app') .controller('AppController', function() { var vm = this; this.doSomething = function () { // 何か移植する価値がありそうなロジック }; }); angular.module('app') .controller('AppController', function(AppControllerHelper) { var vm = this; this.doSomething = AppControllerHelper.doSomething; }); angular.module('app') .factory('AppControllerHelper', function() { return { doSomething: function () { // 何か移植する価値がありそうなロジック }; });
  35. 35. Controllerテストコード移植パス Controller ControllerHelper Service Component ロジックを分離 移植パス 注入(DI) 移行 移植
  36. 36. Controllerテストコード移植パス 移植パス 注入(DI) Controller ControllerHelper Service Component テスト テスト テスト 移行 テスト 新規作成 ロジック部分を移植
  37. 37. まとめ
  38. 38. まとめ - ユニットテストに関するAngular1の知見は再利用できる - Typescriptとzone.jsによりテストコードが書きやすくなっている - Angular1のテスト資産は「移植」する心意気で - 特にControllerのテストコードは注意!!
  39. 39. 参考リンク - Angular 2 unit testing overview - http://www.slideshare.net/mitsuruogawa33/angular-2-unit-testing-overview - Angular 1 to 2 Quick Reference In unit testing - https://gist.github.com/mitsuruog/9e3e5c2c5d17a15a4c2a - Testing you Tasks: Julie Ralph - https://youtu.be/DltUEDy7ItY - mitsuruog/_angular2-unit-test-sample - https://github.com/mitsuruog/_angular2-unit-test-sample - mitsuruog/angular2-minimum-starter: Minimum starter kit for angular2 - https://github.com/mitsuruog/angular2-minimum-starter - I am mitsuruog - Angular2 unit test - https://goo.gl/3ypfaH

×