Java scriptによるテスト駆動開発

Post on 21-May-2015

483 views 0 download

Transcript of Java scriptによるテスト駆動開発

2013/10/23 中村

1.テスト駆動開発とは

2. テスト駆動開発の目的

3. テスト駆動開発の流れ

4.テストフレームワークによるテスト

テスト駆動開発(TDD)とは

テスト用プログラムを書く

テストを行う

リファクタリングによるコードの改善

これを繰り返して開発を進める開発手法

プログラムを外部から見た動作を変えずにソースコードの内部構造を整理すること

コードを追加・変更を繰り返していくうちに重複箇所や煩雑になった場合などの対応すること

他の言語と比べてデバッグしにくい事が挙げられる

エラーが発生しても画面から得られる情報が少ない

テストコードを書き、少しづつ開発を進めていくこの手法が向いていると思われる

1.開発が早くなる

2.クロスブラウザテストが簡単に行える

繰り返し自動でテストが行われることで、正常だったプログラムを修正してバグが発生してしまった場合にもすぐに検知できる。

難易度が高い開発の場合、テストコードを短い間隔で記述して実行していくことにより、整理されていく。その結果、早い開発が可能となる。

テスト駆動開発では常にテストが付きまとう。そのテストが思った通りに動作することに気を付けながら開発を進めていく

その結果・・・

着実に進められるため、結果的にバグによる戻り作業の少ない開発が行える

ツールによって引数にブラウザを設定し、そのブラウザを起動してテストを行える

テストコードを書く、ということは初期投資が必要となる。初期投資が回収出来ないような開発では適用すべきではない

テスト駆動開発は以下の作業サイクルを細かく繰り返してプログラミングを進めていく

テストを書く

不合格確認

合格確認

重複削除のためのリファクタリング

実装する機能を選び、そのための単体テストを書く。短く、単一の振る舞いを対象にするべき

テストコードがバグを含んでいる可能性もある

そういった可能性も考慮して、敢えて不合格テストを実施する。不合格テストを実施することでコードの現在の状態を把握する。

テストを成功させる

重複削除などコードをリファクタリングしてきれいにする。

JavaScriptのテストフレームワークはたくさん存在する

参考として以下に例を紹介する

テストを絶えず実行し作業フローにテストを組み込むことが煩わしい

JsUnit 更新が遅く、新しいプロジェクトに適用するには不向き。Prototype.jsライブラリ

のために作られていたこともあり、依存しがち。

YUI Yahooが作成したブラウザ内テストフレームワーク

QUnit jQueryチームが開発。伝統的なxUnitの設計に厳格に従っていない。

コマンドライン上でテストを実行できるが、本番コードが実行されない環境でのテストとなる

Crosscheck 更新されておらず、現在のブラウザでは対応できない

Rhinoとenv.js JQueryを作った方が開発したライブラリ

以下のツールによる開発を紹介する

JsTestDriver 実際のブラウザ、モバイルでもテスト可能

EclipseやIntelliJ IDEAへのプラグインもある

Junitと互換性のあるXMLレポートの出力も可能。Jenkinsとの連携も簡単に対応できる

しかし古いブラウザに対応していない部分もある。

(XMLHttpRequestオブジェクトを必要とするVar.7

以前のIEには対応できない)

4.1サーバーの起動

4.2ブラウザにアクセス

4.3テスト実行

sudo java -jar

$JSTESTDRIVER_HOME/JsTestDri

ver-1.3.5.jar --port 4224

ブラウザで以下のアドレスにアクセス

http://localhost:4224

cd $JSTESTDRIVER_HOME/files/test-

driven-javascript-development-

code/00-main/case01/

sudo java -jar

$JSTESTDRIVER_HOME/JsTestDriver-

1.3.5.jar --tests all

テストリスト:

①4で割り切れる年はうるう年と判定する

②ただし、100で割り切れる年はうるう年でないと判定する

③ただし、400で割り切れる年はうるう年と判定する

①2004年は4で割り切れるため閏年

②2100年は100で割り切れるため閏年でない

③2000年は100で割り切れるが400で割り切れるため閏年

まずは失敗するパターンで実行する

入力データ:7

// main.js

function isLeapYear(year){

jstestdriver.console.log(year

);

if ((year % 4) == 0) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear':

function(){

var setYears = 7;

assertEquals(true,isLeapYear

(setYears));

}

}

) 入力データ7のケースを追加

Total 1 tests (Passed: 0; Fails: 1; Errors: 0)

(1.00 ms)

Firefox 17.0 Linux: Run 1 tests (Passed: 1;

Fails: 0; Errors 0) (1.00 ms)

isLeapYearTest.test isLeapYear passed

(1.00 ms)

[LOG] 7

次に成功パターンで実行する

入力データ:8

// main.js

function isLeapYear(year){

jstestdriver.console.log(year

);

if ((year % 4) == 0) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear':

function(){

var setYears = 8;

assertEquals(true,isLeapYear

(setYears));

}

}

)

入力データ8のケースを追加

Total 1 tests (Passed: 1; Fails: 0; Errors: 0)

(1.00 ms)

Firefox 17.0 Linux: Run 1 tests (Passed: 1;

Fails: 0; Errors 0) (1.00 ms)

isLeapYearTest.test isLeapYear passed

(1.00 ms)

[LOG] 8

まずは失敗するパターンで実行する

// main.js

function isLeapYear(year){

jstestdriver.console.log(ye

ar);

if ((year % 4) == 0) {

if (year % 100 == 0) {

return false;

}

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear100': function(){

var setYears = 40;

assertEquals(true,isLeapYear(setYears));

},

}

) 入力データ40のケースを追加

両方成功してしまうケース

Total 2 tests (Passed: 2; Fails: 0; Errors: 0)

(0.00 ms)

Firefox 17.0 Linux: Run 2 tests (Passed: 2;

Fails: 0; Errors 0) (0.00 ms)

isLeapYearTest.test isLeapYear4 passed

(0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 passed

(0.00 ms)

[LOG] 40

あ // main.js

function isLeapYear(year){

jstestdriver.console.log(ye

ar);

if ((year % 4) == 0) {

if (year % 100 == 0) {

return false;

}

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear100': function(){

var setYears = 100;

assertEquals(true,isLeapYear(setYears));

},

}

)

入力データ100のケースを追加

こちらは閏年ではないので

Failsで返ってくるはず

Total 2 tests (Passed: 1; Fails: 1; Errors: 0) (1.00 ms)

Firefox 17.0 Linux: Run 2 tests (Passed: 1; Fails: 1; Errors 0) (1.00 ms)

isLeapYearTest.test isLeapYear4 passed (0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 failed (1.00 ms): AssertError: expected true but was false

.test isLeapYear100@http://localhost:4224/test/test/isLeapYear.js:9

[LOG] 100

Firefox 17.0 Linux: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (0.00 ms)

isLeapYearTest.test isLeapYear4 passed (0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 passed (0.00 ms)

[LOG] 40

// main.js

function isLeapYear(year){

jstestdriver.console.log(year);

if ((year % 4 == 0) || (year % 100 != 0)) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYea

rs));

},

'test isLeapYear100': function(){

var setYears = 100;

assertEquals(true,isLeapYear(setYea

rs));

},

}

)

// main.js

function isLeapYear(year){

jstestdriver.console.log(yea

r);

if ((year % 4 == 0) ||

(year % 100 != 0)) {

return true;

}

return false;

}

Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (1.00 ms)

Firefox 17.0 Linux: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (1.00 ms)

isLeapYearTest.test isLeapYear4 passed (0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 passed (1.00 ms)

[LOG] 100

入力データ4は成功(閏年)

入力データ100は失敗(閏年ではない)

となるはず・・・

おかしい

// main.js

function

isLeapYear(year){

jstestdriver.console.log

(year);

if ((year % 4 == 0) &&

(year % 100 != 0)) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYears)

);

},

'test isLeapYear100': function(){

var setYears = 100;

assertEquals(true,isLeapYear(setYears)

);

},

}

)

Total 2 tests (Passed: 1; Fails: 1; Errors: 0) (0.00 ms)

Firefox 17.0 Linux: Run 2 tests (Passed: 1; Fails: 1; Errors 0) (0.00 ms)

isLeapYearTest.test isLeapYear4 passed (0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 failed (0.00 ms): AssertError: expected true but was false

.test isLeapYear100@http://localhost:4224/test/test/isLeapYear.js:8

[LOG] 100

成功!

// main.js

function isLeapYear(year){

jstestdriver.console.log(ye

ar);

if(year % 400){

return true;

} else if ((year % 4 == 0)

&& (year % 100 != 0)) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear100': function(){

var setYears = 100;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear400': function(){

var setYears = 200;

assertEquals(true,isLeapYear(setYears));

},

}

) 入力データ200のケースを追加

こちらは閏年

Total 3 tests (Passed: 1; Fails: 2; Errors: 0) (1.00 ms)

Firefox 17.0 Linux: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (1.00 ms)

isLeapYearTest.test isLeapYear4 passed (0.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 passed (1.00 ms)

[LOG] 100

isLeapYearTest.test isLeapYear400 passed (0.00 ms)

[LOG] 200

再度テストを行い、成功を確認する

// main.js

function isLeapYear(year){

jstestdriver.console.log(ye

ar);

if (year % 400 == 0 ||

(year % 4 == 0 && year %

100 != 0)) {

return true;

}

return false;

}

// isLeapYear.js

TestCase("isLeapYearTest", {

'test isLeapYear4': function(){

var setYears = 4;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear100': function(){

var setYears = 100;

assertEquals(true,isLeapYear(setYears));

},

'test isLeapYear400': function(){

var setYears = 400;

assertEquals(true,isLeapYear(setYears));

},

}

)

Total 3 tests (Passed: 2; Fails: 1; Errors: 0) (2.00 ms)

Firefox 17.0 Linux: Run 3 tests (Passed: 2; Fails: 1; Errors 0) (2.00 ms)

isLeapYearTest.test isLeapYear4 passed (1.00 ms)

[LOG] 4

isLeapYearTest.test isLeapYear100 passed (1.00 ms)

[LOG] 100

isLeapYearTest.test isLeapYear400 failed (0.00 ms): AssertError: expected true but was false

.test isLeapYear400@http://localhost:4224/test/test/isLeapYear.js:13

[LOG] 400

テストを書

不合格確認

合格確認

リファクタリング