C# テスト駆動開発の実践方法:オブジェクトの等価比較をする #11

当連載ではテスト駆動開発を実践するために必要な基礎知識や演習等を紹介しています。現在は実践編として、実際にテスト駆動開発を行いながら、簡単なアプリケーションを構築中です。

これまでのテスト駆動開発のアーカイブは「C# テスト駆動開発の実践方法」で確認できますので、必要に応じてチェックしてみてください。今回も引き続いてテスト駆動開発の実践編に取り組んでいきましょう。この記事ではオブジェクトの等価比較をするための機能を構築していきます。前回から使用しているApp03を使用してアプリケーションを構築していきましょう。

オブジェクトの等価比較とは

オブジェクトの等価比較について、まずは解説していこうと思います。オブジェクトの等価比較とはオブジェクト同士が等価(要するに同じ)であるかを比較するということです。簡単に言ってしまうと数字の1と1は等価ですよね、ということになります。この概念をMoneyクラスのオブジェクトにも適用してしましょうということになります。

お金という概念を考えてみましょう。Aさんの持っている10円とBさんの持っている10円は、10円玉の本体が違えども「価値」は同じですよね。AさんとBさんが10円玉を交換したとしても、価値は同様なのでどちらかが損をしたり、得をしたりするわけではありません。これを等価というわけです。

Moneyクラスから生成されるオブジェクトも、100の値を持つMoneyと100の値を持つMoneyは値が同じであるべきですし、それらを簡単に比較できた方がアプリケーションを構築していくうえで今後も活用できそうですよね。

Moneyの等価を実装する

では早速、Moneyクラスから生成されるオブジェクト同士の比較を実装していきます。基本的にはObjectに備わっているEqaulsメソッドをオーバーライドして作成したいと考えています。とはいえ、まずはテストコードから書いていきましょう。

[TestMethod]
public void MoneyEquals_OK()
{
    var money = new Money(100);
    var otherMoney = new Money(100);
    Assert.IsTrue(money.Equals(otherMoney));
}

上記のようなテストコードになったでしょうか。今回はEqualsというメソッドの実装を考えたいので、Equalsの戻り値であるbool値を判定したいと思いました。ですので、あえてAssertクラスのIsTrueメソッドを使用してテストを判定しています。

ここまでテストコードを書いたら、一度テストを実行してみましょう。元々実装されているので「グリーンになるかな?」と思うかもしれませんが、このMoneyEquals_OKテストは「レッド」になりますね。

これはオブジェクトの等価の判定は「同じインスタンスであるかどうか」を判定しているからです。オブジェクトの「値が同値であるか」ではなく、「オブジェクトが同じか」を判定しているということになります。moneyとotherMoneyは別のインスタンスですので、結果は当然エラーになってしまうのです。

IEquatableを実装する

さて、この「レッド」の状態を「グリーン」にするための作業を行っていきます。オブジェクトの等価を実装するときはIEquatableというインターフェースを実装して、Equalsメソッドをオーバーライドする必要があります。

public class Money : IEquatable

Moneyクラスの定義を上記のようにしてみてください。するとIEquatableインターフェースに赤い波線が引かれると思います。IEquatableにカーソルを移動させ、ctrlを押しながら”.”を押下して、「インターフェースを実装します」を選択しましょう。すると以下のようにメソッドが自動生成されるはずです。

public bool Equals(Money other)
{
    throw new NotImplementedException();
}

ソースコードを変更したので、もう一度テストを実行してみましょう。当然「レッド」のままであることに変わりはありませんが、少し先に進んだらテストを実行して確認しておく、という姿勢は重要です。

ではEqualsメソッドの中身を実装して、テストを「レッド」から「グリーン」にさせるために変更していきます。以下のような感じでEqualsメソッドを実装すればOKになるはずです。

public bool Equals(Money other)
{
    if (other is null) { return false; }
    if (this.Value.Equals(other.Value))
    {
        return true;
    }
    return false;
}

最初に引数であるotherがnullでないことを確認し、そのあとMoneyのValueプロパティとotherのValueプロパティで値の等価判定を実施すればOKです。Moneyクラスの等価は、結局はそれぞれのValueの値が同地であれば等価と判定できるので、比較的簡単な処理といえるでしょう。

コードを変更したのでテストを実施しておきましょう。すると「レッド」の状態から「グリーン」に変わったことが分かると思います。あとはNGパターンのテストコードも実装して試しておきましょう。

[TestMethod]
public void MoneyEquals_NG()
{
    var money = new Money(100);
    var otherMoney = new Money(110);
    Assert.IsFalse(money.Equals(otherMoney));
}

Equalでないことを判定するので、AssertクラスのIsFalseメソッドを使用しています。ここまでくればオブジェクトの等価判定は完了です。足し算・引き算・等価の処理が作成できたので、Moneyクラスに必要な機能はおおむね完成したと言えるでしょう。

次回は口座の残高を記録するための処理の実装に移っていきたいと思います。DBなど難しいことはせずに、テキストファイルで残高を残す簡単な機能ではありますが、引き続きテスト駆動開発で実装していきます。