お題のアプリは『作りながら覚えるAndroidプログラミング』(ソフトバンク・クリエイティブ)という本のStep.6のカウントダウンタイマーを作るというやつです。
本に記載されているコードはこんな感じです。
public class CountdownTimer extends Activity {
// 一部省略
Button startButton;
Button stopButton;
SeekBar seekBar;
public static void countdown(int counter){
// 一部省略
if(counter != 0) {
stopButton.setEnable(true);
startButton.setEnable(false);
seekBar.setEnabled(false);
} else {
stopButton.setEnable(false);
startButton.setEnable(false);
seekBar.setEnabled(true);
}
}
}
まあ、
counter
が0
でなけば、startButton
が触れなくなる。stopButton
が触れるようになる。seekBar
が触れなくなる。
counter
が0
であれば、startButton
が触れなくなる。stopButton
が触れなくなる。seekBar
が触れるようになる。
残年なところはこれ
Activity
の子クラスなので、テストが半端なくめんどくさいのです。というわけで、こういった操作を行うAndroidPOJOなクラスがあると考えてテストしながら作ってみたいと思います。
いや、そもそもView
ってテストできるんだっけ?
これは非常に不安なので、不安を解消するためにテストを書いてみました。
public class CountDownControllerTest extends AndroidTestCase {
public void testCanViewBeToggle() {
Button button = new Button(getContext());
assertTrue(button.isEnabled());
button.setEnabled(false);
assertFalse(button.isEnabled());
}
}
テスト結果
グリーンですね。
大丈夫そうです。
ではおもむろに、期待する振る舞いを書いてみましょう。
CountDownControllerTest
private CountDownControllerTest controller;
private Button startButton;
private Button stopButton;
private SeekBar seekBar;
@Override
public void setUp() thrwos Exception {
super.setUp();
controller = new CountDownController();
startButton = new Button(getContext());
stopButton = new Button(getContext());
seekBar = new SeekBar(getContext());
controller.startButton(startButton);
controller.stopButton(stopButton);
controller.seekBar(seekBar);
}
public void testSetProceeding() {
controller.setProceeding();
assertFalse(startButton.isEnabled());
assertTrue(stopButton.isEnabled());
assertFalse(seekBar.isEnabled());
}
CountDownController
というものがあって、それが画面のView
の操作に関する責務を担うものとします。それらのView
は外(ここではActivity
を想定)から与えられるものとします。そして、操作メソッドはsetProcessing()
とします。最初の段階の
CountDownController
の実装は次のとおりです。CountDownController
public class CountDownController {
public void startButton(Button startButton) {
}
public void stopButton(Button stopButton) {
}
public void seekBar(SeekBar seekBar) {
}
public void setProgressing() {
}
}
テスト結果
まあ、赤ですよ。
そりゃそうですね。何もしていませんから。
View
というかButton
とか、SeekBar
に対して仮の実装はできないので、おもむろに本実装をしてしまいます。CountDownController
public class CountDownController { private View startButton; private View stopButton; private View seekBar; public void startButton(Button startButton) { this.startButton = startButton; } public void stopButton(Button stopButton) { this.stopButton = stopButton; } public void seekBar(SeekBar timeBar) { this.seekBar = seekBar; } public void setProgressing() { startButton.setEnabled(false); stopButton.setEnabled(true); seekBar.setEnabled(false); } }
テスト結果
おっ!グリーン!きましたね。
では、もうひとつのケースを追加しちゃいましょう。
CountDownControllerTest
public void testEndProcessing() {
controller.endProgressing();
assertFalse(startButton.isEnabled());
assertFalse(stopButton.isEnabled());
assertTrue(seekBar.isEnabled());
}
もちろん、仮実装します。コンパイルエラーをなくすための。
CountDownController
public void endProcessing() {
}
テスト結果
ハイ♪ハイ♪ハイ♪ハイ♪ハ~イ♪レッドですよ~♪
というわけで、おもむろに本実装をします。
CountDownController
public void endProcessing() {
startButton.setEnabled(false);
stopButton.setEnabled(false);
seekButton.setEnabled(true);
}
では、テスト実行。
いいね、いいね!グリーン。いいね。
じゃあ、今度は仕様に即してテスト書こうか。
counter
という値に1
を設定した場合はsetProcessing
と同じ結果になるように、counter
という値に0
を設定した場合はendProcessing
と同じ結果になるようにします。まずは、
counter
が1
の場合。CountDownControllerTest
public void testCountDown() {
controller.countDown(1);
assertFalse(startButton.isEnabled());
assertTrue(stopButton.isEnabled());
assertFalse(seekBar.isEnabled());
}
仮実装(コンパイルを通るだけ)にした上で、テストを実行
もちろん落ちるので、仮実装します。
CountDownController
public void countDown(int counter) {
setProcessing();
}
テスト結果
次に
counter
が0
の場合。CountDownControllerTest
public void testCountDownOnEnd() {
controller.countDown(0);
assertFalse(startButton.isEnabled());
assertFalse(stopButton.isEnabled());
assertTrue(seekBar.isEnabled());
}
テスト結果
落ちるので、本格的かつシンプルに実装する。
CountDownController
public void countDown(int counter) {
if(counter == 0) {
endProcessing();
} else {
setProcessing();
}
}
そして、テスト
グリーン。
int
で0
とか1
というきわどい値をやっているので、-1
をやらないわけがない。CountDownControllerTest
public void testCountDownOnException() {
try{
controller.countDown(-1);
fail();
} catch(IllegalArgumentException e) {
assertTrue(true);
}
}
そしてテスト
すぐさま期待する動作になるように実装。
CountDownController
public void countDown(int counter) throws IllegalArgumentException {
if(counter == 0) {
endProcessing();
} else if(counter > 0) {
setProcessing();
} else {
throw new IllegalArgumentException();
}
}
そして、すぐテスト
グリーン!( ゚Д゚ノノ☆パチパチパチパチ
このあと、ユーザーコード(つまり開発者が使うコード)のためにインターフェースを作成したいのだけど、それはみなさん考えてみてください。
こういう感じでAndroidでもTDDができるので、しかもAndroidPOJOでもTDDができるので、ぜひとも取り入れてみてはいかがでしょうか?
0 件のコメント:
コメントを投稿