アーカイブ | 10:01

単体テスト

22 12月

プログラミング始めたての初心者、特に学生さんが相手だったりする場合、いつもこう言っています:

Visual Studio、まずは無料版のExpressでいいですよ。商用利用も可能です。C#に関していうと、コンパイラーの性能制限みたいなのもないです。

 

さて、じゃあ、こんな疑問も浮かぶかと思います:

なんで有償版があるの?それでなんでみんなProfessionalを買うの?

 

その答えの1つが、今日お話しする単体テストです。ということで、今日のお話は、Express版では試せなかったり…。逆に、普段からProfessionalよりも上のグレードを使いこなしてる人には少し退屈かも。

下準備: 今回のテスト対象

今日はベタに、簡単な計算をするメソッドを作って、それの単体テストをしてみましょう。

とりあえず、物自体(つまり、テストの対象)を作ります。UIも何もなし、ライブラリだけ作りましょう。名前は、取り合えず、適当にMyMathとかつけておきます。

 

プロジェクトを作ると、Class1.csというファイルができていると思います。まず、こいつをCalculator.csにリネームしてください。

ソリューション エクスプローラーでリネームすると、ファイル名だけじゃなく、中のクラスもClass1からCalculatorに変わるはずです。

まあ、簡単な例ということで、ただ単に足し算するだけのAddメソッドでも作ってみましょうか。Calculatorクラス内に、以下のようなコードを追加してください。

public static int Add(int x, int y)
{
    return x + y;
}

 

テスト プロジェクト/テスト メソッドを作ってみよう

そして、このAddメソッドを右クリック。コンテキスト メニュー内に、「単体テストの作成」というのがあるので、これをクリックしてください。

以下のようなダイアログ画面が出てくるので、OKを押しましょう。

初回は、「テスト用のプロジェクトを作りますか?」みたいなメッセージが出てくるはずなので、適当な名前を付けて「はい」を押してください。今回は、手抜きもいいところですが、デフォルトでつく名前、TestProject1のままで行きます。

 

以下のようなテスト用メソッドができてるはずです。

[TestMethod()]
public void AddTest()
{
    int x = 0; // TODO: 
適切な値に初期化してください
    int y = 0; // TODO: 
適切な値に初期化してください
    int expected = 0; // TODO: 
適切な値に初期化してください
    int actual;
    actual = Calculator.Add(x, y);
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive(
このテストメソッドの正確性を確認します。);
}

テストになるように、中身を書き換えます。まあ、例えば以下のような感じ。

[TestMethod()]
public void AddTest()
{
    Assert.AreEqual(3, Calculator.Add(1, 2));
// 
本当のテストでは、もっとバリエーション持たせましょうね
}

テストで満たすべき条件を、Assertクラスの各種メソッドを使って書いていきます。この例の場合は、AreEqual、つまり、第1引数と第2引数の値が一致していないといけないという条件です。

テストを走らせてみよう

まあ、この例程度ならテストが通って当然ではありますが… なんにしても、本当にテストが通るか、確認してみましょう。

メニューから[テスト]→[実行]→[ソリューションのすべてのテスト]を選ぶか、Cntrl+R, Aとショートカット キーを押してください。

今回は単純なテストが1個だけなので、一瞬でテストが終わると思います。「テスト結果」ウィンドウが現れます。

成功です!

こまめにテストを走らせると、安心してコードを書き換えられます。

Test First

さて、こんな話もあります: テストを先に書け。

「テストがない」という状況を根絶するための工夫です。

実践してみましょう。まず、テスト クラス内(先ほどのAddTestメソッドの下にでも)に、以下のようなメソッドを追加してください。

[TestMethod()]
public void GcdTest()
{
    var testData = new[]
    {
        new { X = 100, Y = 15, Z = 5 },
        new { X = 144, Y = 81, Z = 9 },
        new { X = 13*11*4, Y = 13*9*5, Z = 13 },
        new { X = 1024, Y = 999*2, Z = 2 },
        new { X = 13*11, Y = 9*8, Z = 1 },
        new { X = 360, Y = 80, Z = 40 },
        new { X = 10, Y = 10, Z = 10 },
    };

    foreach (var item in testData)
    {
        Assert.AreEqual(item.Z, Calculator.Gcd(item.X, item.Y));
        Assert.AreEqual(item.Z, Calculator.Gcd(item.Y, item.X));
    }
}

最大公約数(greatest common divisor、略してGCD)を求める想定で、先にテストだけ書きました。この時点では、まだGcdメソッドも作っていないです。

メソッドがないので当然エラーになるわけですが、Visual Studioを使っていると、エラー個所に以下のようなスマート タグが現れるはずです。

この「メソッド スタブを生成します」を選んでみましょう。Calculatorクラス内に、以下のようなメソッドが自動生成されます。

public static object Gcd(int p, int p_2)
{
    throw new NotImplementedException();
}

じゃあ、実装しましょうか、最大公約数。以下の通り。戻り値の型も、objectからintに変えておきましょう。

public static int Gcd(int x, int y)
{
    while (true)
    {
        var r = x % y;
        if (r == 0) return y;
        x = y;
        y = r;
    }
}

再びテストを実行して、成功を確認しましょう。

Expressの場合: せめてテスト用のコンソール アプリを

この、Visual Studio組み込みの単体テストのいいところは、

  • どの範囲のテストを実行するか選んだりもできる
    • 今、エディター上でカーソルのあるメソッドのテストだけ実行するとか
    • 前回失敗したところだけ実行とかも
  • チーム開発機能と組み合わせて、テストに通ったソース コードしか、管理サーバーにコミット(commit: 約束する、制約する。この場合、「ちゃんとできたから責任もってサーバーに反映するね」という意味)できないようにできる

等々あります。

 

まあ、そこまでやらずとも、とりあえずテスト コードを書くだけなら、適当にコンソール アプリでも作って、そこにテストを書けば大丈夫です。この方法なら、Visual C# Expressでもテストが書けます。

NUnit

ちなみに、NUnitという、オープンソースな単体テスト フレームワークもあって、こいつはExpress版にも対応しているそうなので、ぜひとも導入を検討してみてください。

privateメンバーのテスト

さて、privateなメンバーのテストはどうしましょう?プログラミング言語によっては、「privateだとテストできなくなるし、全部publicで作ればいいんじゃない?」なんていう人もいるみたいです。

が、Visual Studioではそんな心配不要です。Visual Studioが、リフレクションを使ってprivateメンバーに無理やりアクセスするAccessorクラスというのを作ってくれます。

例えば、Calculatorクラスに以下のようなメソッドを追加してみましょう。

private static int PrivateMul(int x, int y)
{
    return x * y;
}

こいつを、右クリックして「単体テストの作成」すると、以下のようなテスト メソッド(と、Accessor)が作られます。

[TestMethod()]
[DeploymentItem(“MyMath.dll”)]
public void PrivateMulTest()
{
    int x = 0; // TODO: 
適切な値に初期化してください
    int y = 0; // TODO: 
適切な値に初期化してください
    int expected = 0; // TODO: 
適切な値に初期化してください
    int actual;
    actual = Calculator_Accessor.PrivateMul(x, y);
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive(
このテストメソッドの正確性を確認します。);
}

Calculator_Accessorというクラスが自動生成されていて、こいつは、Calculatorのprivateメンバーを、リフレクションを使って無理やりpublicにアクセスできるようにしたものです。

便利ツール

最後に、単体テストで使える便利ツールを紹介しておきましょう。

Pex

メソッドの中身に応じてテスト コードを生成してくれるVisual Studioアドインです。テスト漏れが起きないように、きわどいケースなんかを生成してくれます。

(メソッドの中身を気にせず、仕様を元に「このメソッドはこうあるべき」みたいなテストを書くことをブラック ボックス テストと言います。一方、Pexのように、中身を想定して、はまりがちなテスト ケースを書くことをホワイト ボックス テストと言います。)

Moles

Pexの付属品なんですが、標準ライブラリ内のメソッドを自作のものに置き換えてしまう機能を提供するツールです。

例えば、DateTime.Nowのような、常に違う値を返すメソッドを、所望の値を返すように書き換えてしまうというような使い方ができます。

Moq

動的処理の回で書きましたが、テストのお供にモック(mock: 模造品)生成フレームワークがあります。

代表例はMoqなどです。