angular2 rc.1 unit testing overview

39
Angular2-rc.1 Unit testing Overview 2016/05/17 mg-mtg#7 Mitsuru Ogawa(@mitsuruog)

Upload: mitsuru-ogawa

Post on 14-Jan-2017

1.086 views

Category:

Engineering


2 download

TRANSCRIPT

Page 1: Angular2 rc.1 unit testing overview

Angular2-rc.1Unit testing Overview

2016/05/17 mg-mtg#7 Mitsuru Ogawa(@mitsuruog)

Page 2: Angular2 rc.1 unit testing overview

Who am I?

- 小川 充(@mitsuruog)- Front-end engineer@Givery- works

- Angular1.4- ユニットテスト、E2Eテスト

- フロントエンドのCI

Page 3: Angular2 rc.1 unit testing overview

Today’s motivation and goal

− Motivation

 - Release Candidateが近いので、そろそろテストを書くノウハウが必要

 - Angular1のテスト資産をどうするべきか、が心配になってきた

- Goal

 - Angular2でのテストについての概要を知る

 - Angular1のテスト資産の戦略的生かし方

Page 4: Angular2 rc.1 unit testing overview

Table contents

1. Angular2ユニットテストの基本1.1. ツールとセットアップ

1.2. Pipeのテスト

1.3. Serviceのテスト

1.4. Componentのテスト

2. Angular1からの変更点

3. 最近のアップデート情報

4. Angular1テスト資産の生存戦略

以前話した内容の拡大版です

http://goo.gl/1SpphI

Page 5: Angular2 rc.1 unit testing overview

1.Angular2ユニットテストの基本

Page 6: Angular2 rc.1 unit testing overview

ツールとセットアップ

- Typescript- Karma- Jasmine- SystemJS

Karma

SystemJS(モジュールローダー)

SpecApplication

Karma.conf.js =>

Karma-test.shim.js =>

Angular2ユニットテストスタック

Page 7: Angular2 rc.1 unit testing overview

Pipeのテスト

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({

name: 'sayHello'})export class SayHelloPipe implements PipeTransform { transform(value:string):string { return `Hello ${value}`; }}

Page 8: Angular2 rc.1 unit testing overview

Pipeのテスト

import { describe, it, expect, beforeEach } from '@angular/testing';import { SayHelloPipe } from './say-hello.pipe';

テストで利用するモジュールをimportする

Page 9: Angular2 rc.1 unit testing overview

Pipeのテスト

import { describe, it, expect, beforeEach } from '@angular/testing';import { SayHelloPipe } from './say-hello.pipe';

describe('Test: SayHelloPipe', () => {

let testee; beforeEach(() => { testee = new SayHelloPipe(); });

});

インスタンスを取得する

Page 10: Angular2 rc.1 unit testing overview

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'); });

});

テストを実行して結果を比較する

Page 11: Angular2 rc.1 unit testing overview

Serviceのテストimport {Injectable} from "@angular/core";

@Injectable()

export class SayHelloService { constructor() {} say():string { return 'Hello'; }}

Page 12: Angular2 rc.1 unit testing overview

Serviceのテストimport {describe, it, inject, expect, beforeEachProviders} from '@angular/core/testing';import {SayHelloService} from "./say-hello.service";

テストで利用するモジュールをimportする

Page 13: Angular2 rc.1 unit testing overview

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せずそのまま利用する設定)

Page 14: Angular2 rc.1 unit testing overview

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する

Page 15: Angular2 rc.1 unit testing overview

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'); }));});

テストを実行して結果を比較する

Page 16: Angular2 rc.1 unit testing overview

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

Page 17: Angular2 rc.1 unit testing overview

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()); }}

Page 18: Angular2 rc.1 unit testing overview

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する

Page 19: Angular2 rc.1 unit testing overview

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を作成する

Page 20: Angular2 rc.1 unit testing overview

Componentのテスト②describe('Test: SayHelloComponent', () => { let builder; beforeEach(inject([TestComponentBuilder], (tcb) => {

builder = tcb; }));

});

TestComponentBuilderを初期化してCacheしておく

Page 21: Angular2 rc.1 unit testing overview

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を取得する

Page 22: Angular2 rc.1 unit testing overview

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を取得して結果を比較する

Page 23: Angular2 rc.1 unit testing overview

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

Page 24: Angular2 rc.1 unit testing overview

2.Angular1からの変更点

https://goo.gl/lQ4s02

詳しくはこちらへ =>

Page 25: Angular2 rc.1 unit testing overview

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

Page 26: Angular2 rc.1 unit testing overview

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

小さい

大きい

Page 27: Angular2 rc.1 unit testing overview

3.最近のアップデート(テスト周辺)

https://www.ng-conf.org

Page 28: Angular2 rc.1 unit testing overview

- @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

Page 29: Angular2 rc.1 unit testing overview

Test APIの改善

- 非同期処理、変更検知が格段に良くなった

- [RC] injectAsync => async

- [Bata.16] autoDetectChanges() の導入

- 詳しくはJulie Ralphのプレゼンをチェック

- Zone.js!!Zone.js!!Zone.js!!

Testing you Tasks: Julie Ralphhttps://youtu.be/DltUEDy7ItY

Page 30: Angular2 rc.1 unit testing overview

4.Angular1テスト資産の生存戦略

Page 31: Angular2 rc.1 unit testing overview

基本方針

- 移行ではなく「移植」

- 残せるものを残す。残せる部分を増やす。

- 厳しい冬を想定したテストコードの剪定。

- Viewに依存しない「純粋なロジック」のみを移植

- ViewはComponentで書き直すケースが多くなる。

- Directiveに代表されるComponentとして再利用できるものは移植OK。

Page 32: Angular2 rc.1 unit testing overview

ユニットテストコード移植パス

filter pipe

providor, service, factory service

directive

controller

directive

Page 33: Angular2 rc.1 unit testing overview

ユニットテストコード移植パス

filter pipe

providor, service, factory service

directive

controller

directive

Page 34: Angular2 rc.1 unit testing overview

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 () { // 何か移植する価値がありそうなロジック

}; });

Page 35: Angular2 rc.1 unit testing overview

Controllerテストコード移植パス

Controller

ControllerHelper Service

Component

ロジックを分離

移植パス

注入(DI)

移行

移植

Page 36: Angular2 rc.1 unit testing overview

Controllerテストコード移植パス

移植パス

注入(DI)

Controller

ControllerHelper Service

Componentテスト

テスト

テスト

移行テスト

新規作成

ロジック部分を移植

Page 37: Angular2 rc.1 unit testing overview

まとめ

Page 38: Angular2 rc.1 unit testing overview

まとめ

- ユニットテストに関するAngular1の知見は再利用できる

- Typescriptとzone.jsによりテストコードが書きやすくなっている

- Angular1のテスト資産は「移植」する心意気で

- 特にControllerのテストコードは注意!!

Page 39: Angular2 rc.1 unit testing overview

参考リンク

- 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