为什么这个断言在比较结构时抛出格式exception?

我试图断言两个System.Drawing.Size结构的平等,我得到一个格式exception,而不是预期的断言失败。

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

这是预期的行为? 我在这里做错了什么?

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

问题是有两个级别的string.Format在这里进行。

格式化的第一个级别是这样的:

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

然后我们使用string.Format和你提供的参数:

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

(显然有文化提供,某种消毒……但还不够。)

这看起来很好 – 除非预期值和实际值本身在转换为一个string之后最终带有大括号 – 它们是为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); 

…那就是失败 哎哟。

事实上,我们可以通过欺骗格式来使用我们的预期和实际部分的参数来certificate这一点:

 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的相当可怕的上下文。

作为解决方法,您可以像StriplingWarriorbuild议的那样使用string.Format 。 这就避免了使用实际值/期望值对格式化结果执行的第二级格式化。

我想你已经find了一个错误。

这工作(抛出一个断言exception):

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

我同意@StriplingWarrior,这确实看起来是至less有两个重载Assert.AreEqual()方法的错误。 正如StiponeWarrior已经指出的那样,下面的失败

 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[] 。 从技术上讲,是的结构对象…但特殊types的对象,即值types。 我认为这是错误所在。 如果我们使用与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); } 

我认为第一个断言是不正确的。

用这个代替:

 Assert.AreEqual(struct1, struct2, string.Format("Failed expected {0} actually is {1}", struct1, struct2));