94

我试图断言两个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

我懂了。是的,这是一个错误。

问题是这里有两个层次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}

所以第二级格式化是这样的:

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 的建议使用。这避免了对使用实际/预期值格式化的结果执行第二级格式化。

于 2013-02-19T20:42:44.337 回答
43

我想你发现了一个错误。

这有效(引发断言异常):

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));

但这不起作用(抛出 a 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 的观点,这确实似乎是 Assert.AreEqual() 方法在至少 2 个重载上的错误。正如 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 是一个结构。对象呢?参数列表确实string指定消息之后的列表是params object[]. 从技术上讲,是的结构对象......但是特殊类型的对象,即值类型。我认为这就是错误所在。如果我们使用我们自己的对象,其用法和结构与 类似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 回答