我正在尝试断言两个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);
}
这是预期的行为吗?我在这里做错了吗?
答案 0 :(得分: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建议。这避免了对使用实际/期望值进行格式化的结果执行第二级格式化。
答案 1 :(得分: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));
但这不起作用(抛出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));
答案 2 :(得分: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是一个结构。对象怎么样?参数列表 指定string
消息后的列表为params object[]
。从技术上讲,是结构是对象...但对象的特殊种,即值类型。我认为这就是bug的所在。如果我们使用具有与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);
}
答案 3 :(得分:3)
我认为第一个断言不正确。
请改用:
Assert.AreEqual(struct1,
struct2,
string.Format("Failed expected {0} actually is {1}", struct1, struct2));