お題のアプリは『作りながら覚える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に対して仮の実装はできないので、おもむろに本実装をしてしまいます。CountDownControllerpublic 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 件のコメント:
コメントを投稿