はじめまして。アプリケーション共同開発部 東京開発課の沖田と申します。
突然ですが「テスト」書いてますか。
私は7月に入社してから Xamarin に触れているのですが、初めての環境でまだ右往左往しております。
右往左往している時は自分の書いたコードが動いていても不安になることが少なくないので、今回テストを書きたいと思い、Xamarin でユニットテストをする方法を調べてみました。
一昔前までは正直なところ「テスト」はそれほど注目は浴びていなかったと思います。(「一昔前」の定義によりますが)
しかし CI (Continuous Integration) が普及するに従い徐々に注目を集めるようになり、今となっては長期間に渡って高品質なプロダクトを維持していくためには必須の知識・技術と言っても過言ではありません。
テストには様々な種類や方法がありますが、今回は Xamarin (Xamarin Studio) を用いた開発で、比較的手軽に行える、クライアントアプリのユニットテスト、つまりメソッド単位のテストについて簡単にではありますが、紹介させていただきたいと思います。
テストを書く理由
当ブログでも度々登場している Xamarin は、主に iOS や Android といったモバイルアプリのクロスプラットフォーム開発環境です。
モバイルアプリに限った話ではありませんが、クロスプラットフォームでの開発は、どれだけコードを共通化できるかというのが大切な要素です。この点に関する設計が充分でないとプラットフォーム依存のコードが多くなり、クロスプラットフォーム開発環境の優位性を十分に発揮することができません。
そのため、十分に考えられた構成のプロジェクトであれば、自然とプラットフォーム依存のコードは減り、共通して使うことのできるコードが増えていきます。そして、その共通で使うことができるコードは、結果的に影響範囲も広く使用頻度も高くなります。
つまり、コードの共通化というのは開発の期間を短縮できる可能性のある非常に魅力的な言葉ですが、逆にそれだけ重要度や難易度が高いということでもあるのです。
そのような重要な処理に対してテストコードを書くということは、処理を十分に検討・確認することを意味し、結果としてバグも少なく高品質なアプリケーションの開発につながります。
そしてこれらは、当然 Xamarin を使った場合でも同じ事が言えます。
また Xamarin に限らず、開発している最中やアップデートの際の仕様変更などで、既存の処理を修正する必要が出てくることも少なくありません。
その時にテストコードがあれば、変更した処理が想定外の影響を及ぼしていないか確認することができるため、エンバグの可能性も減らすことができます。
これまで、プログラマーの間ではテストコードの有用性は理解されていましたが、工数や予算の都合などで割愛されてしまうことも少なからずありました。
しかし、昨今のアプリケーション開発は外見も中身もクオリティが高いものが求められます。
そのクオリティを維持するためには、わかりやすいクラウドサービスや見た目が派手な UI ライブラリー以外にも、このような一見地味なテストの重要性を、エンジニアはもちろん、エンジニア以外も理解しておくことは非常に大切だと言えるでしょう。
ユニットテストを書いてみる
それでは実際にサンプルプロジェクト(ソリューション)を一つ作成し、そのユニットテストを書いてみましょう。
今回は将来的に iOS 固有の処理もテストできるように、iOS ベースのユニットテストプロジェクトを作成し説明させていただきます。
また、既存のプロジェクトにテストを追加するケースを想定して、まず最初にベースとなるプロジェクトを作成し、そこにユニットテストを追加する流れで説明します。
ベースのプロジェクトの作成
まずはベースとなるプロジェクト(ソリューション)を作成します。
そしてさらにテストするメソッドを用意しましょう。今回はデフォルトで作成される MySampleApp 以下の MyClass に下記のようなメソッドを記述しました。
using System; namespace MySampleApp { public class MyClass { public static int Max(int a, int b) { return a > b ? a : b; } } }
ユニットテストプロジェクトの作成
次にユニットテスト用のプロジェクトを作成します。今回は iOS の中に含まれる Unit Test App を選択し、AppName には適当なものを入れて進めます。今回は MySampleApp.Test.iOS としました。
これでユニットテストを書く下準備は整いました。
ここまでの状態で一度ユニットテストのプロジェクトを実行してみましょう。先ほど作成した MySampleApp.Test.iOS プロジェクトを右クリックし「この項目を実行」を選択します。
iOS のシミュレータが起動し画面のように表示されれば OK です。
ユニットテストのコードを書く
さてそれではいよいよテストコードを書いてみましょう。
テスト自体の大まかな流れは下記のようになります。
1. テストコードを記述するクラスを作成し、テストコードを記述します
2. テストを実行します
3. 必要に応じてコードを修正します
4. 再度実行し修正を確認します
今回は先程作成した MyClass の Max メソッドをテストしてみます。
この Max メソッドは与えられた 2 つの引数の大きい方を返すというものです。
まずは、テストを記述するためのクラスを作成します。
先ほど作成した MySampleApp.Test.iOS 以下にファイルを作成し、参照に MyClass のある MySampleApp を追加します。
今回は Fixtures.cs という名前のファイルを作成し下記のようなコードを記述しました。
TestFixture や Test という記述が、テストの実行対象となるためのアトリビュートです。
using System; using NUnit.Framework; using MySampleApp; namespace Fixtures { [TestFixture] public class Tests { [Test] public void TestMax() { // 0と1の比較 Assert.IsTrue(MyClass.Max(0, 1) == 1); // 引数を逆にして確認 Assert.IsTrue(MyClass.Max(1, 0) == 1); // 正と正の比較 Assert.IsTrue(MyClass.Max(1, 2) == 2); // 負と負の比較 Assert.IsTrue(MyClass.Max(-2, -1) == -1); // 正と負の比較 Assert.IsTrue(MyClass.Max(1, -1) == 1); // 同じ値は? Assert.IsTrue(MyClass.Max(1, 1) == 1); // 最小値と0 Assert.IsTrue(MyClass.Max(int.MinValue, 0) == 0); // 最大値と0 Assert.IsTrue(MyClass.Max(int.MaxValue, 0) == int.MaxValue); } } }
上記のテストコードを書いて、再度テストプロジェクトを実行します。
Run Everything をタップしてテストを実行し、問題がなければ緑色の文字の部分に Success! と表示されるはずです。
テストが成功すれば問題ありませんが、もし失敗した場合はテストコードかテスト対象のメソッドに間違いがあるはずです。メソッドの実装だけでなくテスト判定にも間違いがある可能性はあるので、テストに失敗した場合はメソッドの実装とテストコードの両方を確認しましょう。
テストに失敗した場合は、Xamarin のログを見ても良いですし、テストアプリケーション内に表示される内容でもどこでテストに失敗したかが分かります。
今回の Max メソッドは非常に単純なものですが、場合によってはもっと複雑な実装の場合もあるかもしれません。そのような時でも上記のようなテストが書かれていたら安心して使うことができますし、実装を変更する場合でもテストの成否を確認すれば安心して修正を入れることができます。
とはいえ上記のケースは Max メソッドの実装に対してやや大げさすぎる感もあります。実際にどの程度のケースを網羅するかは、実装の複雑度などに応じて考える必要があるでしょう。
Assert の種類
成否を判定するための Assert はその判定方法で多くの種類があり、場合によって使い分けると便利です。
全部は紹介しきれませんが、まず最初に下記のものが使えるようになると便利かと思いますので、ぜひ参考にして下さい。
Assert の種類 | 判定内容 |
---|---|
Assert.AreEqual(a, b) | a と b が同じなら成功、違っていたら失敗 |
Assert.AreNotEqual(a, b) | a と b が違っていたら成功、同じなら失敗 |
Assert.IsNull(a) | a が null であれば成功、null でなければ失敗 |
Assert.IsNotNull(a) | a が null でなければ成功、null であれば失敗 |
Assert.IsTrue(a) | a が true であれば成功、true でなければ失敗 |
Assert.IsFalse(a) | a が false であれば成功、false でなければ失敗 |
https://developer.xamarin.com/api/type/NUnit.Framework.Assert/
どのようなテストコードを書けばよいか
たとえば数値を引数に取る場合は、値の下限・上限、さらに結果が変化する閾値の前後の値、などを確認することが多くなります。Nullable なパラメータの場合には null のケースも確認すると良いでしょう。
どのようなテストコードを書けば良いか最初のうちは悩みがちですが、基本的にはメソッドの仕様をテストコードで表すつもりで書くのがオススメです。
そしてさらにオススメなのは、メソッド本体より先にテストコードを書いて、できればレビューもしてもらうことです。
そうすれば、テストコードでメソッドの仕様をある程度理解してもらえるので、コードのみならず仕様もレビューしてもらえることになります。この流れはテスト初心者のみならず、プログラミングの経験が浅い人にとっても安心の材料です。
テストアプリの Options について
これはまだ自分も調査中なので深くは語れないのですが、テストのログを取るために便利そうな機能なので、軽く紹介させていただきます。
テストアプリケーションを実行した際に気がついた方もいると思いますが、表示される項目の中に Options というものがあり、この項目をタップすると REMOTE SERVER と DISPLAY という設定項目の画面になります。
この設定は、テストの結果をサーバーに送信するためのパラメータで、任意の値を設定できます。REMOTE SERVER の Enable をオンにした場合、テスト実行時に自動的にサーバーに接続するようになるので、不要な場合はオフのままにしておきましょう。
また、この値はコードからも取得できます。
using MonoTouch.NUnit.UI; ... TouchOptions options = TouchOptions.Current; /* options.ShowUseNetworkLogger // REMOTE SERVER - Enable の値 options.HostName // REMOTE SERVER - Host Name の値 options.HostPort // REMOTE SERVER - Port の値 options.SortNames // DISPLAY - Sort Names の値 */
サーバーへのログ送信をしなくとも、この値をテストのパラメータとして使うことも可能なので、更に調査して活用したいと思います。
Xamarin Test Cloud について
今回はメソッドの動作を確認するためのユニットテストについて説明しましたが、Xamarin では UI テストをクラウドで実行できるサービスがあります。ただしクラウドと言ってもテストが実行されるのはその先に接続されている実機です。
この Xamarin Test Cloud を使うためには有料契約の必要がある点がネックですが、2000 種類以上のデバイスを使うことができるとのことなので、それを考えると自分で揃えるよりははるかに安く済ませることができます。海外端末が多いようですが、手元に用意できない端末のテストを行いたいときは非常に有用な選択肢です。(※有料契約は必要ですが無料の試用期間も設けられています)
興味のある方は下記のリンクを参考にしてみてください。
https://www.xamarin.com/test-cloud
さいごに
今回、簡単にではありますが Xamarin でのユニットテストについて紹介させていただきました。
興味のあるかたは下記の公式ドキュメントからさらに詳細な情報を見ることができます。
https://developer.xamarin.com/guides/ios/deployment,_testing,_and_metrics/touch.unit/
自分もまだまだ勉強中の身ですので、今後もクオリティの高いアプリケーションの開発を目指し更に勉強していきたいと思います。
今後ともどうぞ宜しくお願い致します!
フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!