BDD by Jasmine (jscafe 13)

36
BDD by jasmine Ryuma Tsukano jsCafe13

description

javascriptでjasmine使ってBDDする話。

Transcript of BDD by Jasmine (jscafe 13)

Page 1: BDD by Jasmine (jscafe 13)

BDD by jasmineRyuma Tsukano

jsCafe13

Page 2: BDD by Jasmine (jscafe 13)

● BDDについて● jasmineの基本文法● TDDのプロセスをハンズオン

今日の目次

Page 3: BDD by Jasmine (jscafe 13)

● javascript上でBDDをするためのtest framework○ assertion/testdouble等、testに必要な機能全部入ってる

● 主にUTをカバーするもの

=>BDDとは?

jasmineとは

Page 4: BDD by Jasmine (jscafe 13)

BDDとは

Page 5: BDD by Jasmine (jscafe 13)

Behaivor Driven Development● 振る舞い 駆動 開発● Dan North氏が提唱したもの。

振る舞い=そのシステムの動作仕様の事● ①specification : 詳細設計付近で定義されるような細かな動作の事● ②story : 要求仕様付近のアプリの動作の事※後述するが、それぞれツール群が異なる

=>つまり、仕様から始める開発方法● 当たり前。● =>なぜ、こんな事を言ってるか?

BDD

Page 6: BDD by Jasmine (jscafe 13)

元々BDDはTDDから派生したもの。

● TDD:テスト駆動開発 by Kent Beck ->本

幾つか代表的な特徴を上げると、● テスト作業をプログラムで書く● 実装の前にテストを書く(Test First)● 初めに通過するcodeを書いてその後書き直して行く(Refactoring)

いわゆるxUnitが普及=>Junit始め各言語で出てきた。

Dan North=TDDの教育者。教えた現場の中で、● TDDが上手くいく現場と、● TDDが上手く行かない現場があった。

Dan NorthとTDD

Page 7: BDD by Jasmine (jscafe 13)

<BAD> TDDがうまくいかない現場の様子● 何を書くかで悩む● 工数が膨らむ?

原因● 彼らは「テスト」の観点でTDDを実施していた。

<GOOD>TDDがうまくいっている現場の様子● 何を書くかで悩まない● 工数が削減?● 副産物の効果に気づいてる(後述)

原因● 彼らは「設計」の観点でTDDを実施していた。● (&結果としてテスト)も出来た。

2patternの現場

Page 8: BDD by Jasmine (jscafe 13)

TDDの観点として本当の意味でのテストをすると...● 何をtestするか?で悩む● 膨大な量のテストを書く事に● 自動化難しいtestも書く?● 追加改修時直す所沢山

=>とても大変な事に!

また、TDD = テストと認識すると=>別の手段で詳細設計LVの事をしてしまう現場も有り=>TDD=設計(※後述)とすると、2重に設計作業してる事になる。=>設計で2倍の工数がかかる。

TDDとテスト

引用元:http://www.hayst.com/Pages/positioning.aspx

Page 9: BDD by Jasmine (jscafe 13)

TDDしてる瞬間● test書いてる時

○ これから書くソースのロジック考える■ =>詳細設計/プログラム設計してる

● test書いた後○ これを残してあげてる事で次の人に役立つ。like API仕様書

■ =>詳細設計/API仕様書/IF仕様etc書いてる

=>実は設計してる(&結果として、それがテストの一部に)=>TDDが上手く機能している現場は、これに気づいていた。

TDDする時にテストから設計にフォーカスを移すと...

TDDと設計

テスト

設計

Page 10: BDD by Jasmine (jscafe 13)

TDDで設計すると(テストするTDDと比べて)● 観点が明快なのでテストソース作りで悩む事が少なくなる。

● テストの量が効果的に減る○ テストソースも見通しが良くなる○ 後での変更の量も少なめ

● 結果として後でも仕様として意味のあるテストが残る○ TDDでテスト:仕様として冗長で分かり辛い○ TDDで設計:第3者が見て仕様を把握し易い

● 位置づけを設計と明示=>事前に詳細設計的な作業を含まない○ =>さっきのような誤って工数2倍が起こり辛い

実はKentBeckもTDDはテストというより設計(分析)と言ってる=>設計を前面にした方が正しい。

TDDで設計する効果

Page 11: BDD by Jasmine (jscafe 13)

TDD(Test Driven Development) = テスト < 設計なんじゃん!● テストって頭につくから勘違いされて失敗するんじゃんか。● 仕様/設計っぽい言葉(振る舞いBehaivor)を入れようぜ!

=>そこでBehaiver Driven Development

BDD(特にUT系のtool)の特徴(TDDと比べて)● 自然言語で動作振る舞いを書く

○ TDD:method => BDD:describe “person” .. it “should have pen”..● 自然言語っぽく確認メソッドも書く

○ TDD: assert => BDD:shouldやexpect● 自然言語の順で検証する「[実際値]は[期待値]と等しい」

○ TDD:assert_equals 期待, 実際 => BDD:expect実際toBeEqual期待● 自然言語っぽく前提条件、後処理

○ TDD : setup/teardown => BDD : before/after※TDD系のToolもBDDっぽくなってるので、一概には言えないが...=>いずれにせよ自然言語的に振る舞う事を前面に出してる点が特徴

Dan North

Page 12: BDD by Jasmine (jscafe 13)

仮にテストする事を数値化したとすると● 100%:やらなくてはいけないテスト

○ 60%:BDDが結果としてやるテスト(仕様として意味有)○ 残り=40%。残ってる!この扱いは考えないといけない

残りの40%に対しての色んな考え方● ①テストしない(勿論、機能試験や受け入れ試験は実施するが)

○ 考え方:40をどこまで減らせる?に注力○ 「不安がある所をテスト(BDD)する」

● ②昔ながらのSIっぽい網羅的な手動テストする○ User多いor社会的な影響大きいと必然こうなる

● ③99%全部自動化する○ 元の話に戻るが、こういう現場も○ 保守/追加改修で、ただのテスト部分(上記の40)が邪魔になる

■ この部分(40)を後で削除する人達もいる=>自動化無意味?○ 保守の為のTDD?設計の為のTDD?testの為の...とか言ってる

=>求められている品質、納期に基づいて調整を

結局、テストはどこにいった?

Page 13: BDD by Jasmine (jscafe 13)

● 1)各言語でBDDツール出来た○ ‘03 jBehave(java)から、C#版、php版、python版諸々出た○ 初めはstory形式(given when then) => 結合テストより上

● 2)rspec(ruby)が流行った。○ 結局、1のツールは普及しなかったが、rspecが流行った。○ rspecはspecifications形式(like TDD) => 単体テストをカバー

● 3)cucumber(ruby)が出てきた。○ 2)のrspecのstory runnerの話が膨らんでcucumberとして独立○ 1のstory形式をより洗練化=>表向き全部自然言語で書く

■ そこそこ流行った。

● 4)jasmine(javascript)が出てきた。○ 元々jsUnit(xUnit系)作ってたPivotal社が改めて作った。≒ rspec

※最近jsではmocha(+chai等)が勢い有。BDDで書けばjasmineと大体一緒

BDDのその後の動き

Page 14: BDD by Jasmine (jscafe 13)

jasmine

Page 15: BDD by Jasmine (jscafe 13)

jasmineを始めましょう。

公式でふたつあり1. stand alone・・・js単体で動作する物2. ruby gem版・・・rails等ruby系frameworkの中で使うための物

1を使おう。

公式ページの一番下のDownloadsの「stand alone release」をクリックしてダウンロード

=>解凍してみよう!

Let’s start jasmine

Page 16: BDD by Jasmine (jscafe 13)

本体(lib/specRunner)と、サンプルのjs(src)とその仕様(spec)● lib : jasmineの本体。実行に必要なファイル。

○ jasmine-html.js : html上のreport生成○ jasmine.css : そのcss○ jasmine.js : jasmine本体

● spec : サンプルの仕様(test)○ PlayerSpec.js : サンプルのPlayer関数に対してのspec(test)○ SpecHelper.js : 全体的な設定等を記載する所

● src : サンプルのjsファイル○ Player.js : サンプルのPlayer関数(≒class)○ Song.js : サンプルのPlayerの使ってるSong関数(あえて開発中)

● SpecRunner.html : テスト結果を表示する(サンプルを実行)

stand alone版

Page 17: BDD by Jasmine (jscafe 13)

ブラウザでSpecRunner.jsを表示してみる=>サンプルのテストを実行

その結果● 抹茶色の帯

○ 5つの仕様(spec)の確認に成功したと書かれている● その下

○ 振る舞いが書かれている○ ex)PlayerはSongをplayできる○ ex)songを停止した時

■ songが現在停止を示す■ 再開する事が出来る..

=>Jasmine実行結果から仕様が分かる

SpecRunner.js

Page 18: BDD by Jasmine (jscafe 13)

さっき、表示していたのは、コレ。describe("Player", function() { var player; var song;

beforeEach(function() { player = new Player(); song = new Song(); }); it("should be able to play a Song", function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song);

//demonstrates use of custom matcher expect(player).toBePlaying(song); });

spec/PlayerSpec.js

Page 19: BDD by Jasmine (jscafe 13)

● describe : suites○ 意味のある仕様(spec)の集合

■ ≒ディレクトリ的なもの。名前○ describeはnestできる○ >第1引数:タイトルやパターン名等○ >第2引数:function(){ ※specs(複数の仕様)を書く }○ 例)describe(“Player”, function() { …

■ describe(“when song has been paused”, function() { ...

● it : specs○ 仕様

■ ここにexpectaions(振る舞いの期待)を書く○ >第1引数:どう振る舞うか自由に記述○ >第2引数:function() { ※expectaion(振る舞い期待)を書く}○ 例)it(“should be able to play a Song” , function() { ...

describeとit

Page 20: BDD by Jasmine (jscafe 13)

expectation : 振る舞いの記述● expect(実際の値).マッチャー(期待値)

例)playerのcurrentlyPlayingSongプロパティにsongが入ってる事を確認したい

it("should be able to play a Song", function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song);

=>意図通り :SpecRunner.htmlでpass=>誤っている:SpecRunner.htmlでfailure● 試しに、上のsongを”fake”にしてみると..

expectation

Page 21: BDD by Jasmine (jscafe 13)

書き方:to□□□□(期待値)● expect(x).toEqual(y) : xはyと等しい● expect(x).toBeTruthy() : xはtrueである● expect(x).toContain(y) : xはyを含む(x = 配列か文字列)● expect(x).toBeGreaterThan(y) : xはyより大きい● 他、多数

自作matcherも作れる

matcher

Page 22: BDD by Jasmine (jscafe 13)

addMatchers({ 自作matcher名 : function(期待値) { ... ※この中でactual = 実際の値を使える return 真偽 }});=>全体で使いたければ、SpecHelper.jsに書く

例)現在の歌と期待値が同じ、かつ演奏中である事を確認するmatcherthis.addMatchers({ toBePlaying: function(expected) { var player = this.actual; return player.currentPlayingSong === expected && player.isPlaying; }}); // これが、src/SpecHelper.jsに書いてある

自作matcher

Page 23: BDD by Jasmine (jscafe 13)

前提条件● beforeEach(function () { … 前提条件となる処理 } );

○ 後処理は、同じ書き方でafterEach

例)前提条件としてテスト対象となるplayer/song関数をnewする beforeEach(function() { player = new Player(); song = new Song(); });

実行順)itの度にbeforeEachする

describe(“suites”, function() {[a] beforeEach(function() { ... })[b] it(“specs-first”, function() { … });[c] it(“specs-second”, function() { … });

=> 実行順 [a] => [b] => [a] => [c]

前提条件

Page 24: BDD by Jasmine (jscafe 13)

spy : test doublesを実現するための仕組み● test doubles=テスト対象が依存してるcomponentを置換する仕組み

テスト対象だけを確認したいのに、依存する外部のコンポーネントに何か理由があって、確認が出来ない時に、外部のコンポーネントを別のものに置換する、というもの。

実際はfake/dummy/mock/stub/spyと色々あるがjasmineでは意識しない

jasmineとtest doubles

テストソースjasmine

テスト対象js

依存する外部のコンポーネント

js

Page 25: BDD by Jasmine (jscafe 13)

● まだ実装されてないコードの代替○ 実際、これが一番一般的なシチュエーション?

● slow test対策○ 特にDB等ioに関する処理は遅い=>遅い所を置換する

● 外部のリソースアクセス○ 例)某位置SNSのAPIよく落ちてる=>testの成否が不安定化

■ このAPIを置換しておけばUTの結果は安定● ※UnitTestはこれでいいが、このエラーはエラーで対応考

えないといけないのでご注意を。

● 純粋なUnitTestをするため○ 教科書的にはComponent TestとUnit Testは違う

test doublesはいつ使う?

Page 26: BDD by Jasmine (jscafe 13)

● spyOn(対象constructor, 関数名)○ =>これで実装がどうであれ仮の関数が作り出される

● expect(実際の値).toHaveBeenCalledWith(期待する引数)○ =>期待する引数で仮の関数を使っている事を確認する

it("tells the current song if the user has made it a favorite", function() { // ★これ (src/Song.js見るとpersistFavoriteStatusはエラーを返す) spyOn(song, 'persistFavoriteStatus');

player.play(song); player.makeFavorite(); // この中でsong.persistFavoriteStatusを実行

// ★これ (player.makeFavoriteの中でtrue引数で実行を確認) expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);

jasmineのtestdouble

Page 27: BDD by Jasmine (jscafe 13)

spyオブジェクト.andReturn(期待する戻り値)

例) spyOn(song, 'persistFavoriteStatus').andReturn("success!");

expect(song.persistFavoriteStatus()).toEqual("success!");

※この例はあまり意味が無いけど、前述のように例えばtestする関数が使用してる別の関数が出来てない時等で使う

spy.andReturn

Page 28: BDD by Jasmine (jscafe 13)

TDD(BDD)のプロセスをハンズオン

Page 29: BDD by Jasmine (jscafe 13)

今からサンプルにソースを追加してBDDの一通りの流れを見てみる

<やりたい事>Player.jsに● volumeプロパティを追加● setVolume()を追加

○ setVolume()にvolumeが0〜100の範囲かのvalidationを追加○ この範囲内:volumeプロパティ=値を入れて「true」を返す○ この範囲外:「false」を返す

※setterでtrue/false返すのおかしくないか?的なツッコミは置いておいて!気にしないで!>_<ただの例なの!

BDDしてみよう

Page 30: BDD by Jasmine (jscafe 13)

失敗=>成功=>Refactoring

TDDの流れ

RED(fail)

GREEN(pass)Refactor

Start

Page 31: BDD by Jasmine (jscafe 13)

TDDの流れ● 0:やるべきタスクを整理

○ todoを作る(by kent beck) => jasmineのdescribe/itで良いかと

● 1:仮実装(Fake it)○ Red)仮のtest書く=>元が無いので失敗○ Green)とにかくtestに通過する仮の実装をする○ Refactor)一旦整理

● 2:三角測量(triangulate)=>明白な実装(obvious implementation)○ Red)三角測量するtestを書く=>失敗

○ Green)実際の処理を実装(明白な実装)=>成功○ Refactor)汚くなったら整理

その後、適宜Refactoring。もし、何かしらの原因でRedになったらGreenになるべく調整

具体的な流れ

Page 32: BDD by Jasmine (jscafe 13)

①−1)// spec/PlayerSpec.js 最後の括弧とじの前に以下を追加 describe("#setVolume", function() { it("can set 80", function() { expect(player.setVolume(80)).toEqual(true); }); });=> SpecRunner.html : Red(失敗)

①−2)// src/Player.jsPlayer.prototype.setVolume = function() { return true;};=> SpecRunner.html : Green(成功)

このガリガリのハードコーディングが仮実装

※specの確認をしてる。正しい値があればspec動くか?の確認

①仮実装

Page 33: BDD by Jasmine (jscafe 13)

同じテスト対象に対して異なる入力値を与える(=三角測量)②−1)// spec/PlayerSpec.js it("can not set 200(>= 100)", function() { expect(player.setVolume(200)).toNotEqual(true); });=> SpecRunner.html : Red(失敗)

②−2)ここで正しく実装する。(=明白な実装)// src/Player.jsPlayer.prototype.setVolume = function(volume) { this.volume = volume; if(0 <= volume && volume <= 100){ return true; } else { return false;}};=> SpecRunner.html : Green(成功)

②三角測量=>明白な実装

Page 34: BDD by Jasmine (jscafe 13)

todo=>仮実装=>三角測量=>明白な実装● TDD(BDD):設計で悩むときは、頭の中で考えるより、とりあえず手を動か

していった方がいい。○ そのための、プロセスを提示している

● 逆に、特に悩む事が無ければ、いきなり明白な実装しても良い○ 絶対的なルールではない。

それぞれ意味がある● todo

○ やる事を整理● 仮実装

○ 正しいテストを作る● 三角測量

○ 入力値にはパターンがある事を認識● 明白な実装

○ 上記踏まえて実装

TDDの進め方

Page 35: BDD by Jasmine (jscafe 13)

BDDとは● TDDから派生。● テストでなく設計を前面に出したもの。

○ =>そうした方が悩まなくて済む

jasmine● describe { }とit { }で囲む● expect( … ).マッチャーで確認● spyでtest double

TDDのプロセスハンズオン● 基本:テスト失敗=>成功=>リファクタリングの繰り返し● 流れ:いきなり明白な実装しても良いが、悩んだら、以下

○ todo整理=>仮実装=>三角測量=>明白な実装

まとめ

Page 36: BDD by Jasmine (jscafe 13)

おしまい