none
単体テストで、クラス同士や構造体同士の比較 RRS feed

  • 質問

  • Visual Studio 2010で、単体テストプロジェクトを作っているのですが

    困ったことが・・・・

    Assert.AreEqualやCollectionAssert.AreEqual等で、
    変数単体や、配列の比較は出来たのですが、構造体やクラスの比較が出来なくて困っています

    public ClassA{
    int a;
    int b;
    .....//メンバ変数いろいろ
    }

    ClassA InputData=new ClassA ();
    ClassA OutputData=new ClassA ();

    InputData.a=100;
    ...

    OutputData = func();

    等として、比較したいときに
    Assert.AreEqual(InputData,OutputData);
    としても、こちらのもくろみ通りの値が設定されていても、不一致NGになってしまいます

    クラス同士や構造体同士でメンバの比較をしたいときは、メンバ一つずつにAssertを使用する必要があるのでしょうか

    2014年1月9日 11:11

回答

  • テスト以前に通常はどのように比較されていたのでしょうか? 一般論として、IEquatable<T>インターフェースがあります。単体テストと関係なくインスタンスが等しいかどうかを定義できるのであれば、このインターフェースを実装すればいいでしょう。
    単体テストでのみ比較を行いたいのであれば、単体テスト内にIEqualityComparer<T>インターフェイスを実装したクラスを用意しておき、これを使って比較すればいいでしょう。その時はAssert.IsTrue()とかを使うことになるのかな…?
    # 通常は前者になり、テスト時のみ振る舞いが異なるということは稀だと思います。
    • 回答の候補に設定 星 睦美 2014年1月14日 2:12
    • 回答としてマーク 星 睦美 2014年1月16日 5:27
    2014年1月9日 12:31
  • 参照型(class)のインスタンスの比較の場合、そのままでは参照の比較(≒ポインタの比較)になってしまうので、オブジェクトの意味的な等値性の比較を実装するにはObject.Equals()とObject.GetHashCode()をオーバーライドします。調べればすぐにサンプルや注意点は見つかると思いますが、もしIEquatable<T>を実装するならばObject.Equals()とObject.GetHashCode()のオーバーライドも忘れずに。値型もしくはStringのみを含む値型(struct)をAssert.AreEqual()で比較するだけであればEquals()のオーバーライドは不要です(本番コードで比較が必要な場合は普通オーバーライドします)。

    もしテスト対象クラスもしくはそのクラス内部で使われているクラスのソースコードが修正できないのであれば公開されているフィールドorプロパティを外部で1つずつ比較するほかないですが……

    	public struct SVector2D
    	{
    		public double X;
    		public double Y;
    		// 浮動小数点数の誤差許容値を独自に定める場合は Equals() をオーバーライドするべき。
    	}
    
    	public struct TestStruct1
    	{
    		public SVector2D Position;
    		public string Name;
    		// HACK: 値型のフィールドにどうしても参照型を含めたい場合、Equals() をオーバーライドするべき。
    		// http://msdn.microsoft.com/ja-jp/library/2dts52z7(v=vs.110).aspx
    	}
    
    	public class CVector2D : IEquatable<CVector2D>
    	{
    		public double X { get; set; }
    		public double Y { get; set; }
    		public bool Equals(CVector2D other)
    		{
    			return other != null && this.X == other.X && this.Y == other.Y;
    		}
    		public override bool Equals(object obj)
    		{
    			return this.Equals(obj as CVector2D);
    		}
    		public override int GetHashCode()
    		{
    			// ハッシュ値作成の簡易実装。
    			// http://msdn.microsoft.com/ja-jp/library/system.object.gethashcode(v=vs.100).aspx
    			return this.X.GetHashCode() ^ this.Y.GetHashCode();
    		}
    	}
    
    	public class TestClass1 : IEquatable<TestClass1>
    	{
    		public CVector2D Position { get; set; }
    		public string Name { get; set; }
    		public bool Equals(TestClass1 other)
    		{
    			return other != null && this.Position.Equals(other.Position) && this.Name == other.Name;
    		}
    		public override bool Equals(object obj)
    		{
    			return this.Equals(obj as TestClass1);
    		}
    		public override int GetHashCode()
    		{
    			return this.Position.GetHashCode() ^ this.Name.GetHashCode();
    		}
    	}

    • 編集済み sygh 2014年1月9日 13:44
    • 回答の候補に設定 星 睦美 2014年1月14日 2:13
    • 回答としてマーク 星 睦美 2014年1月16日 5:27
    2014年1月9日 13:11

すべての返信

  • テスト以前に通常はどのように比較されていたのでしょうか? 一般論として、IEquatable<T>インターフェースがあります。単体テストと関係なくインスタンスが等しいかどうかを定義できるのであれば、このインターフェースを実装すればいいでしょう。
    単体テストでのみ比較を行いたいのであれば、単体テスト内にIEqualityComparer<T>インターフェイスを実装したクラスを用意しておき、これを使って比較すればいいでしょう。その時はAssert.IsTrue()とかを使うことになるのかな…?
    # 通常は前者になり、テスト時のみ振る舞いが異なるということは稀だと思います。
    • 回答の候補に設定 星 睦美 2014年1月14日 2:12
    • 回答としてマーク 星 睦美 2014年1月16日 5:27
    2014年1月9日 12:31
  • 参照型(class)のインスタンスの比較の場合、そのままでは参照の比較(≒ポインタの比較)になってしまうので、オブジェクトの意味的な等値性の比較を実装するにはObject.Equals()とObject.GetHashCode()をオーバーライドします。調べればすぐにサンプルや注意点は見つかると思いますが、もしIEquatable<T>を実装するならばObject.Equals()とObject.GetHashCode()のオーバーライドも忘れずに。値型もしくはStringのみを含む値型(struct)をAssert.AreEqual()で比較するだけであればEquals()のオーバーライドは不要です(本番コードで比較が必要な場合は普通オーバーライドします)。

    もしテスト対象クラスもしくはそのクラス内部で使われているクラスのソースコードが修正できないのであれば公開されているフィールドorプロパティを外部で1つずつ比較するほかないですが……

    	public struct SVector2D
    	{
    		public double X;
    		public double Y;
    		// 浮動小数点数の誤差許容値を独自に定める場合は Equals() をオーバーライドするべき。
    	}
    
    	public struct TestStruct1
    	{
    		public SVector2D Position;
    		public string Name;
    		// HACK: 値型のフィールドにどうしても参照型を含めたい場合、Equals() をオーバーライドするべき。
    		// http://msdn.microsoft.com/ja-jp/library/2dts52z7(v=vs.110).aspx
    	}
    
    	public class CVector2D : IEquatable<CVector2D>
    	{
    		public double X { get; set; }
    		public double Y { get; set; }
    		public bool Equals(CVector2D other)
    		{
    			return other != null && this.X == other.X && this.Y == other.Y;
    		}
    		public override bool Equals(object obj)
    		{
    			return this.Equals(obj as CVector2D);
    		}
    		public override int GetHashCode()
    		{
    			// ハッシュ値作成の簡易実装。
    			// http://msdn.microsoft.com/ja-jp/library/system.object.gethashcode(v=vs.100).aspx
    			return this.X.GetHashCode() ^ this.Y.GetHashCode();
    		}
    	}
    
    	public class TestClass1 : IEquatable<TestClass1>
    	{
    		public CVector2D Position { get; set; }
    		public string Name { get; set; }
    		public bool Equals(TestClass1 other)
    		{
    			return other != null && this.Position.Equals(other.Position) && this.Name == other.Name;
    		}
    		public override bool Equals(object obj)
    		{
    			return this.Equals(obj as TestClass1);
    		}
    		public override int GetHashCode()
    		{
    			return this.Position.GetHashCode() ^ this.Name.GetHashCode();
    		}
    	}

    • 編集済み sygh 2014年1月9日 13:44
    • 回答の候補に設定 星 睦美 2014年1月14日 2:13
    • 回答としてマーク 星 睦美 2014年1月16日 5:27
    2014年1月9日 13:11
  • ステファンタラリコ さん、こんにちは。
    フォーラム オペレーターの星 睦美です。

    今回の質問に参考になったのではないかと思います。私から[回答としてマーク] させていただきましたが
    もし回答の内容に質問がありましたら、遠慮なく返信いただければと思います。

    それでは今後ともMSDN フォーラムをお役立てください。


    フォーラム オペレーター 星 睦美 - MSDN Community Support

    2014年1月16日 5:31