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/testdriven-javascript-developmentcode/00-main/case01/

 sudo

java -jar
$JSTESTDRIVER_HOME/JsTestDriver1.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;
}
// main.js
function isLeapYear(year){
jstestdriver.console.log(yea
r);
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));
},
}
)








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){

// isLeapYear.js
TestCase("isLeapYearTest", {
'test isLeapYear4': function(){
var setYears = 4;

jstestdriver.console.log
(year);
if ((year % 4 == 0) &&
(year % 100 != 0)) {
return true;
}
return false;
}

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/isLea
pYear.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/isLea
pYear.js:13

[LOG] 400
テストを書
く
リファクタリン
グ

不合格確認

合格確認

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