xUnit.netを使って、独自クラスに対してAssert.Equalする

スティングフレームワークとしてxUnit.netを使ったところ、独自クラスに対してAssert.Equalする時に少し悩んだのでメモを残しておきます。

 

環境

 

悩んだところ

独自クラスをプロパティとして持つクラスを用意します。

public class Target
{
    public Pair KeyValuePair { get; private set; }

    public void SetKeyValue(string key, string value)
    {
        KeyValuePair = new Pair()
        {
            Key = key,
            Value = value
        };
    }  
}

public class Pair
{
    public string Key { get; set; }
    public string Value { get; set; }
}

 
このクラスのSetKeyValueメソッドの動作をテストするため、以下の様なテストコードを書きました。

const string HogeKey = "hoge";
const string FugaValue = "fuga";

[Fact]
public void FailTest()
{
    var expected = new Pair()
    {
        Key = HogeKey,
        Value = FugaValue
    };

    var actual = new Target();
    actual.SetKeyValue(HogeKey, FugaValue);

    Assert.Equal(expected, actual.KeyValuePair);
}

 
テストを実行すると失敗しました。各プロパティの値は一致していますが、参照が異なるせいか別モノと認識されているようです。

f:id:thinkAmi:20150509070642p:plain

 

対応

Assert.Equalメソッドを定義しているEqualityAsserts.csのコードを読むと、Assert.Equalにはオーバーロードがいくつかあり、その内の1つにIEqualityComparer<T>を引数に持つものがありました。

そのため、まずはIEqualityComparerを実装したクラスを作りました。

public class PairComparer : IEqualityComparer<Pair>
{
    public bool Equals(Pair x, Pair y)
    {
        // 2つのオブジェクトを等しいとみなす条件
        return x.Key.Equals(y.Key, StringComparison.Ordinal) &&
            x.Value.Equals(y.Value, StringComparison.Ordinal);
    }

    public int GetHashCode(Pair pair)
    {
        // Equals対象のプロパティのHashのXORをHashCodeとして返す
        return pair.Key.GetHashCode() ^ pair.Value.GetHashCode();
    }
}

 
次に、Equalメソッドの第三引数に上記のクラスを渡すよう修正したところ、expectedとactualが同じものと認識され、テストが通りました。

Assert.Equal(expected, actual.KeyValuePair, new PairComparer());

 

ソースコード

GitHubに上げておきました。
thinkAmi-sandbox/xUnit-TwoObjectsEquality-sample

 

参考

IEqualityComparerを使う方法

 

文字列の比較

 

GetHashCodeについて