94

2つの構造の同等性をアサートしようとしてSystem.Drawing.Sizeいますが、予期されたアサートの失敗ではなく、フォーマット例外が発生します。

[TestMethod]
public void AssertStructs()
{
    var struct1 = new Size(0, 0);
    var struct2 = new Size(1, 1);

    //This throws a format exception, "System.FormatException: Input string was not in a correct format."
    Assert.AreEqual(struct1, struct2, "Failed. Expected {0}, actually it is {1}", struct1, struct2); 

    //This assert fails properly, "Failed. Expected {Width=0, Height=0}, actually it is {Width=1, Height=1}".
    Assert.AreEqual(struct1, struct2, "Failed. Expected " + struct1 + ", actually it is " + struct2); 
}

これは意図された動作ですか?私はここで何か間違ったことをしていますか?

4

4 に答える 4

100

私はそれを持っている。そして、はい、それはバグです。

問題は、ここで2つのレベルがstring.Format進行していることです。

フォーマットの最初のレベルは次のようなものです。

string template  = string.Format("Expected: {0}; Actual: {1}; Message: {2}",
                                 expected, actual, message);

次にstring.Format、指定したパラメーターを使用します。

string finalMessage = string.Format(template, parameters);

(明らかに、提供されている文化と、ある種の消毒があります...しかし十分ではありません。)

文字列に変換された後、期待値と実際の値自体が中括弧で囲まれていない限り、これは問題ないように見えますSize。たとえば、最初のサイズは次のように変換されます。

{Width=0, Height=0}

したがって、フォーマットの2番目のレベルは次のようになります。

string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " +
              "Message = Failed expected {0} actually is {1}", struct1, struct2);

...そしてそれが失敗しているのです。痛い。

実際、フォーマットをだまして、予想される部分と実際の部分にパラメーターを使用することで、これを非常に簡単に証明できます。

var x = "{0}";
var y = "{1}";
Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar");

結果は次のとおりです。

Assert.AreEqual failed. Expected:<foo>. Actual:<bar>. What a surprise!

私たちが予想していfooなかったし、実際の値もなかったので、明らかに壊れていますbar

基本的に、これはSQLインジェクション攻撃に似ていますが、。のそれほど怖くないコンテキストですstring.Format

string.Format回避策として、 StriplingWarriorが提案するように使用できます。これにより、実際の値/期待値を使用したフォーマットの結果に対して実行される第2レベルのフォーマットが回避されます。

于 2013-02-19T20:42:44.337 に答える
43

バグを見つけたと思います。

これは機能します(assert例外をスローします):

var a = 1;
var b = 2;
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

そしてこれは機能します(メッセージを出力します):

var a = new{c=1};
var b = new{c=2};
Console.WriteLine(string.Format("Not equal {0} {1}", a, b));

しかし、これは機能しません(をスローしますFormatException):

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

これが予想される動作になる理由は考えられません。バグレポートを提出します。それまでの間、回避策は次のとおりです。

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, string.Format("Not equal {0} {1}", a, b));
于 2013-02-19T19:34:12.300 に答える
5

@StriplingWarriorに同意します。これは、少なくとも2つのオーバーロードでAssert.AreEqual()メソッドのバグのように見えることです。StiplingWarriorがすでに指摘しているように、次のことは失敗します。

var a = new { c = 1 };
var b = new { c = 2 };
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

私は、コードの使用法をもう少し明確にするために、これについて少し実験を行ってきました。以下も機能しません。

// specify variable data type rather than "var"...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

// specify variable data type and name the type on the generic overload of AreEqual()...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual<Size>(a, b, "Not equal {0} {1}", a, b);

これは私に考えさせました。System.Drawing.Sizeは構造体です。オブジェクトはどうですか?param liststring、メッセージの後のリストがであると指定しますparams object[]。技術的には、yes構造体オブジェクトです...しかし、特殊な種類のオブジェクト、つまり値型です。これがバグのあるところだと思います。と同様の使用法と構造を持つ独自のオブジェクトを使用する場合、Size実際には次のように機能します。

private class MyClass
{
    public MyClass(int width, int height)
        : base()
    { Width = width; Height = height; }

    public int Width { get; set; }
    public int Height { get; set; }
}

[TestMethod]
public void TestMethod1()
{
    var test1 = new MyClass(0, 0);
    var test2 = new MyClass(1, 1);
    Assert.AreEqual(test1, test2, "Show me A [{0}] and B [{1}]", test1, test2);
}
于 2013-02-19T20:26:53.950 に答える
3

最初のアサートは正しくないと思います。

代わりにこれを使用してください:

Assert.AreEqual(struct1, 
                struct2, 
                string.Format("Failed expected {0} actually is {1}", struct1, struct2));
于 2013-02-19T19:20:26.583 に答える