Qiita C# Advent Calendar 2014 16日目のエントリーになります。
どうも、僕です。ワクワクするコード書いてますか?
NUnitのテストがオールグリーンになる瞬間ってワクワクしますよね。
ということで、NUnitのTestCaseの作り方について。
適当なテストのターゲットとして、以下のようなTimeCheckerクラスの
IsValidTimeメソッドを想定しておきます。
ただ単に時間が0-23の範囲で分が0-59の間であることを確認するだけのもの。
public class TimeChecker { public bool IsValidTime(int hour, int minute) { return 0 <= hour && hour <= 23 && 0 <= minute && minute <= 59; } }
ベーシックなテストの書き方からいくと、このメソッドのテストは
たとえば以下のような感じになる。
[TestFixture] public class TimeCheckerTest { [Test] public void 時刻の値として2時30分が正しいことを判定する() { Assert.IsTrue(new TimeChecker().IsValidTime(2, 30)); } [Test] public void 時刻の値として2時90分が正しくないことを判定する() { Assert.IsFalse(new TimeChecker().IsValidTime(2, 90)); } }
さてここで、NUnit 2.5からはTest Case属性が使えるので、
これを利用しない手はないですね。
TestCaseを使って書き直すとこんな感じ。
[TestFixture] public class TimeCheckerTest { [TestCase(2, 30, Result = true, TestName = "2:30")] [TestCase(2, 90, Result = false, TestName = "2:90")] public bool 時刻の値が妥当かどうかを判定する(int hour, int minute) { return new TimeChecker().IsValidTime(hour, minute); } }
さらに、TestCase属性でパラメータを指定する代わりに
TestCaseSourceを使えば、コンパイル定数でない値をパラメータに使えたり、
より柔軟な書き方が可能になる。
TestCaseSourceは、メソッドのパラメータの順番通りに値を返すものでされあれば
object[]とかでもいいのだけど、ここはNUnitで用意してくれている
TestCaseDataクラスを使いましょう。
[TestFixture] public class TimeCheckerTest { private static TestCaseData[] isValidTestSource = new[] { new TestCaseData(2, 30).Returns(true).SetName("2:30"), new TestCaseData(2, 90).Returns(false).SetName("2:90"), }; [TestCaseSource("isValidTestSource")] public bool 時刻の値が妥当かどうかを判定する(int hour, int minute) { return new TimeChecker().IsValidTime(hour, minute); } }
ここからようやく本題。
TestCaseSourceはIEnumerableでさえあればいいので、
たとえばプロパティにして↓こんなふうに書いても良いのだ。
private static IEnumerable<TestCaseData> IsValidTestSource { get { yield return new TestCaseData(2, 30).Returns(true).SetName("2:30"); yield return new TestCaseData(2, 90).Returns(false).SetName("2:90"); } }
んでまあ必要性からいえば、せいぜい
0:00, 23:59, -1:00, 0:-1, 24:59, 23:60
あたりをテストしておけば十分でしょうけど、
んなちいせぇことを言わず、なんなら正常パターン全部並べたって
こんな程度ならたいして実行時間かかんないわけですよ。
テストケース作るのだって全然簡単!
private static IEnumerable<TestCaseData> IsValidTestSource { get { for (int h = 0; h < 24; h++) { for (int m = 0; m < 60; m++) { yield return new TestCaseData(h, m) .Returns(true) .SetName(h + ":" + m.ToString("D2")); } } } }
実行してみる。
ひゃっはー。1440ケース!
補足ですが、実はNUnitの機能としてパラメータにRangeだとかValuesだとかっていう
属性を指定することができて、今回くらいの例であれば
Enumerateとかするまでもなく同じことが可能だったりする。
ですがまあ、yield returnで動的にテストケースを組み立てていけるよっていうのは
知っておくと多少は便利かと思います。
あと一応注意点としては、「動的に」とか言ったけども
テストが実行されるのは、すべてのテストケースの列挙が終わった後なんで、
同じ変数の値を入れ替えつつyield returnとかやってると
アレ?ってことになりがちです。