【セミナー】セミナーフォロー@高崎(MSDN オフラインセミナー 全国ツアー)

こんにちは。昨日は北関東支店(高崎)にて、全国ツアーを開催しました。ご参加いただいた皆様、ありがとうございました。

※本セミナーから、Visual Studio Team System 2008 RTM 版をお披露目しました(^^

さて、単体テストの基本についてのしょっぱなのデモンストレーションにて、本来テストが成功するはずのところが、失敗してしまいましたが、その場では原因がわかりませんでした。

セミナー終了後に、単体テストをデバッグつきで実行したら、なんてことはない、すぐに原因がわかりました(^^;

この投稿では、何が失敗したのかと、デバッグ実行の仕方、そして原因箇所をお知らせします。

■どんなアプリケーションだったか?
このテスト対象のアプリケーション(というかクラス)は銀行口座を扱うもので、BankAccountというクラス名です。BankAccountには、預金残高をあらわすフィールド(_currentBalance)とローン残高をあらわずフィールド(_currentLoad)があります(それぞれに読み取り専用のプロパティ CurrentBalance と CurrentLoan もあります)があり、メソッドとして預け入れを行う DepositMonet() と引き出しを行う MakePayment() があります。

クラス図で見るとこんな感じです:

image

■テスト対象は?
今回のテスト対象メソッドは、預け入れを行う DepositMoney() です。引数に預金したい額を入れるとその額が預金残高に追加されるというものです。

単体テストの作成は、対象メソッドを右クリックして「単体テストの作成」を行うだけです。スケルトンが以下のようにはかれるので:

/// <summary>
///DepositMoney のテスト
///</summary>
[TestMethod()]
public void DepositMoneyTest()
{
    BankAccount target = new BankAccount(); // TODO: 適切な値に初期化してください
   
    float depositAmount = 0F; // TODO: 適切な値に初期化してください
   

    target.DepositMoney(depositAmount);
   

    Assert.Inconclusive("値を返さないメソッドは確認できません。");
}

こちらに対して、テストの前提となるお膳立て、テストしたい引数(預金したい額)、そして期待したい結果を入力していきます:

/// <summary>
///DepositMoney のテスト
///</summary>
[TestMethod()]
public void DepositMoneyTest()
{
    BankAccount target
        = new BankAccount(100.00F, 40.00F); // TODO: 適切な値に初期化してください
   

    float depositAmount = 30.00F; // TODO: 適切な値に初期化してください
   
    float expected = 100.00F + 30.00F;

    target.DepositMoney(depositAmount);

    Assert.AreEqual(expected, target.CurrenctBalance, "預金額が一致しません。");
    // Assert.Inconclusive("値を返さないメソッドは確認できません。");
}

赤太字の箇所が今回追記した場所になります。普通に考えたら、100 ドルの預金残高があるときに、30 ドルを預け入れしているので、期待する結果は、130 ドル。当然結果も 130 ドル となるわけですね。

それが、今回なぜか、結果が、130 ドルではなく、100 ドルとなり、テストが失敗してしまいました(^^;

■デバッグしてみましょう
ここで昨日もデバッグすればすぐに原因はわかったのですが口頭でも言ったようにほかにもっとお伝えしたいことがあったので、今回は原因追及をあきらめました。

デバッグの仕方ですが、まず、プロダクションのコード DepositMoney() にブレークポイントを設定します:

image

次に、単体テストをデバッグ実行します。いろいろな方法でデバッグ実行はできます。

メニューからは、[テスト]:[デバッグ]:[現在のコンテキストのテスト]

image

ショートカットキーでは [Ctrl+R][Ctrl+T] です。テストビューからもツールバーからも image で行えます。

さて、デバッグ実行すると、なんと!実は、この DepositMoney() ですが、ローン残高がある場合に預け入れを行うと、預金残高に加算するのではなく、まずローンを返済する(ローンが完済されたら残額を預金残高に加算する)というロジックだったのです!!!いやー、作った本人ですが、すっかり忘れておりました(^^;

ですから、結果としては、預金残高は、変更なし(100 ドルのまま)で、ローン残高が 40 ドルから 10 ドルになっていたんです。そんな DepositMoney() のコードは以下になります(^^;

public void DepositMoney(float depositAmount)
        {
            // ローンがある場合は、ローンを返済して残りを預金する
            if (this._currentLoan > 0)
            {
                // ローンを一括返済できる場合
                if (this._currentLoan <= depositAmount)
                {
                    this._currentBalance += depositAmount - this._currentLoan;
                    this._currentLoan = 0;
                }
                // ローンを部分的に返済する
else
{
this._currentLoan -= depositAmount;
}
            }
            // ローンがない場合は、残額を預金残高に追加する
            else
            {
                this._currentBalance += depositAmount;
            }
        }

今回のテストでは、上記コードの赤字のところを通っていたのです!!!

ちなみにこちらは、コードカバレッジの結果からもどこを通ったのかはわかりますので、デバッグしなくてもおそらく原因はすぐにわかったことでしょう。

ちなみに、今回は仕様をど忘れした私の完全なミスですが、このように思った動きと違う動きをすると感じることは開発現場ではよくあることですね(作り手と使い手が異なる場合にですが(^^;)。

■ほんとはどんなテストケースにすべきだったのか?
本当は、ローン残高がゼロな状態であのテストをしたかったのです。もしくは、ローンがあるため、ローン返済が行われるテストとして、Assert を

Assert.AreEqual(expectedForBalance, target.CurrenctBalance, "預金残高が一致しません。");
Assert.AreEqual(expectedForLoan, target.CurrentLoan, "ローン残高が一致しません。");

とすべきでした(expectedForBalance と expectedLoan の float 型で適切なものをセットしておく)。

ということで、Visual Studio の挙動がおかしいとかそういう類ではありませんのでご安心ください。というか不審に思われてしまった方がいらっしゃいましたら、すみませんでした。何がおかしかったかというと私がおかしかっただけです(^^;

さて、この一連のソースコードですが、何らかの形で皆様にご提供することを考えています。ソースコードの提供だけではなんですので、一連のデモシナリオもご提供をと考えております。

提供しましたら、また別の投稿でお知らせしますので、今しばらくお待ちください。
※デモソースコードをフォローアップページにて公開しました(2007/12/20 19:00 追記)

ながさわ