Unifinity Studioで始めるテスト駆動開発

テックポエム

皆さんは、「テスト駆動開発」という言葉をご存知でしょうか?


今回のテーマはUnifinity Studioでのテスト駆動開発です。初めて聞いた方でも理解できる内容になっていますので、どうぞ最後までお付き合いください。
簡単に説明すると、まずテストを作り、そのテストにパスするコードを作成する。このサイクルを繰り返して開発を進めるのがテスト駆動開発です。
今回は、例題として0…nの自然数を入力して、nに対応するフィボナッチ数を返すロジックを作成します。
フィボナッチ数(Fibonacci number)は、イタリアの数学者レオナルド・フィボナッチにちなんで名付けられた数です。
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, …
最初の2項は 0, 1 であり、以後どの項もその直前の2つの項の和となっています。
隣同士の項は必ず互いに素であり、比は黄金比に収束するなどの特徴を備えた神秘的な数(列)です。また、花びらの数など、自然界にもよく現れることで有名な数です。

前置きが長くなりましたが、Unifinity Studioでテスト駆動開発を始めましょう!
最初に例題であるフィボナッチ数を返すロジックを作成します。
想定しているテストは期待値0のfibonacci(0)です。
最初の内容は以下のようになります。

image1.png

最初のコードは数値を入力として受け取りますが、すべて定数の0を返します。
驚かれる読者もあるかと思いますが、これがテスト駆動開発です。
テストの結果がパスであれば、コードの内容や手段は問いません。
これがテスト駆動開発の重要なポイントとなります。

フィボナッチ数を返すロジックができたので、画面に入力用のテキストコントロール、処理を行うボタン、テスト用のボタン、結果を出力するラベルを配置します。
以下のような画面になるかと思います。

image2.png

続けて、メインとなるロジックを作成します。処理を行うボタンの実行処理から呼び出されます。名前は0_MainLogicとしました。
このロジックの3.で先ほど作成したフィボナッチ数を返すロジックを呼び出し、5.で結果をラベルに出力します。
処理の前後にトースト表示を入れておくと便利です。トースト表示は実運用環境では無効のチェックを入れるだけで実行されませんので便利です。
内容は以下のようになると思います。

image3.png

それでは、今回のテーマであるテスト駆動開発用のロジックを作成しましょう。
最初にアサート(Assert)処理ロジックを作成しましょう。多くの開発環境で用意されている有名な関数(クラス)です。
2つの数値を比較して等しい場合は1, 等しくない場合は0を返すシンプルなロジックです。

image4.png

次にテスト駆動の本体を作成しましょう。名前は98_TestFibonacciとしました。
まずは、期待値が0, fibonacci(0)のテストを作成します。以降のテストの追加では以下の1…8をコピー&ペーストで追加してパラメーター部だけ変更します。
image5.png
最初のテストは期待値0でfibonacci(0)なので、2.と3.のパラメーターに数値の0を指定します。
テストを行うボタンの実行処理に98_TestFibonacciを指定して実行してみましょう。

image6.png

成功しましたか?おめでとうございます!これでテスト駆動開発の最初のステップは完了です。

続いて、期待値が1, fibonacci(1)のテストケースを作成します。(1…8を選択してコピー&ペーストすると簡単です)
変更点は10, 11のパラメーターです。それぞれを数値の1に変更して実行してみましょう。

image7.png

エラーダイアログが表示されたでしょうか?
そうです、原因は1_Fibonacciが定数の0を返していることです。
テスト結果が失敗(エラー)なので、1_Fibonacciが引数0以外の場合に1を返すコードを追加しましょう。

1_Fibonacciを以下のように変更しました。0の場合だけを特別扱いし、それ以外の場合は定数1を返すコードになっています。

image8.png

テストを実行してみましょう。成功しましたか?
おめでとうございます。期待値が1の場合もパスすることができました。
テストの結果がパスであれば問題ありません。次にすすみましょう。

次に98_TestFibonacciに新しいテストを追加しますが、テストを増やす毎にコピー&ペーストしてパラメーターを書き換えるのは面倒です。
テスト本体をリファクタリングしたいところですが、スペースが足りませんので、別の機会にしたいと思います。テスト駆動開発においてもリファクタリングは非常に重要な概念となります。

それでは、新しいテストを追加していきましょう。
次は期待値1、fibonacci(2)のテストです。
98_TestFibonacciの1…8をコピー&ペーストしてパラメーターの18を数値の2を指定、19を数値の1と指定します。

image9.png

テストを実行してみてください。驚かれた読者も多いのではないでしょうか。なぜかテストはパスするのです。パスした場合は、コードを変更する必要はありません。先に進みましょう。

先ほどのテストはパスしたので、更にテストを追加します。次は期待値が2のfibonacci(3)です。

image10.png

どうでしょう?今度はエラーになったかと思います。
テストの結果が失敗(エラー)であれば、パスするようにコードを変更します。
先ほどは0を特別扱いしましたので、今度は1(2以下)を特別扱いする戦略で修正しましょう。

image11.png

8.で定数の2を結果としましたが、これは実は 1 + 1のことなのです。
既に気が付かれた読者もいると思いますが、一般化することができます。
そうです、フィボナッチ数は「最初の2項は 0, 1 であり、以後どの項もその直前の2つの項の和となっている。」という性質があります。
1 + 1 の前の方の1は fibonacci(n - 1)なので、 fibonacci(n - 1) + 1となります。
そして後ろの1はfibonacci(n - 2)なので、fibonacci(n - 1) + fibonacci(n - 2)となります。

fibonacci(2)の場合の条件分岐に関しても、もう少し整理できそうです。
可能な限りシンプルにしましょう。上記8の処理を一般化することができます。
以下のようなコードで表現することができそうです。

image12.png

それではテストを実行してみましょう。成功のトーストが表示されましたか?
おめでとうございます!駆け足ではありましたが、Unifinity Studioでテスト駆動開発を行い、n番目のフィボナッチ数を得るロジックを作成することができました。(注意:このアルゴリズムは指数関数時間となり実用的ではありませんので、注意してください。)

まずテストを作成して、そのテストにパスするコードを作成する。この繰り返しのリズム感が、テスト駆動開発の重要なポイントです。
ロジックの最適化、入力のバリデーション、UIUXデザイン、処理中の内容表示等、まだまだ手を入れたいところはありますが、今回はこの辺で筆を止めたいと思います。


今回の「動作するきれいなコード」を作成する方法が、読者へのクリスマスプレゼントになれば幸いです。それではみなさま、良いクリスマスを!

[参考文献]
Kent Beck, 和田卓人訳, Test-Driven Development By Example, テスト駆動開発, オーム社, ISBN 978-4-274-21788-3, 2017 年
Kent Beckが、XPのプラクティスに提唱しているテスト駆動開発を解説した本です。付録 Bのフィボナッチを参考にしました。
冒頭で「動作するきれいなコード」がテスト駆動開発のゴールであると宣言されています。


テスト駆動開発に興味を持たれた方はご一読いただければと思います。