94

Ich versuche, die Gleichheit von zwei System.Drawing.Size Strukturen zu behaupten, und ich bekomme eine Format Ausnahme anstelle der erwarteten Assert-Fehler.Warum löst diese Assertion beim Vergleich von Strukturen eine Formatausnahme aus?

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

Ist das beabsichtigte Verhalten? Mache ich hier etwas falsch?

+0

haben Sie versucht, 'Assert.AreEqual (struct1, struct2, string.Format (" Fehlgeschlagen erwartet {0} ist eigentlich {1} ', struct1.ToString(), struct2.ToString()))?" – DiskJunky

+0

That funktioniert gut, aber ich bin neugierig, warum Assert.AreEqual() kann nicht eine Zeichenfolge mit Strukturtypen formatieren – Kyle

+0

@Kyle Aus Neugier, ist dies nicht mit der Silverlight-kompatiblen Version des Unit Testing-Framework, ist es ? Ich kann es mit diesen DLLs reproduzieren (habe noch nicht die volle .NET-Framework-Version ausprobiert) EDIT: vergiss es, auch mit den vollen getestet und immernoch fehlgeschlagen. :) –

Antwort

100

Ich habe es. Und ja, es ist ein Fehler.

Das Problem ist, dass es hier zwei Ebenen von string.Format geht.

Der erste Niveau der Formatierung ist so etwas wie:

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

Dann benutzen wir string.Format mit den Parametern, die Sie geliefert haben:

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

(Offensichtlich gibt es Kulturen vorgesehen ist, und einige Art der Desinfektion ... aber nicht genug.)

Das sieht gut aus - es sei denn, die erwarteten und tatsächlichen Werte selbst enden mit geschweiften Klammern, nachdem sie in eine Zeichenkette konvertiert wurden - was sie für Size tun. Zum Beispiel endet die erste Größe bis zu Umgewandelt:

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

... und das ist, was andernfalls:

{Width=0, Height=0} 

So ist die zweite Ebene der Formatierung etwas wie ist. Autsch.

der Tat können wir das wirklich leicht beweisen, indem sie die Formatierung täuscht unsere Parameter für die erwarteten und tatsächlichen Teile zu verwenden:

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

Das Ergebnis ist:

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

klar gebrochen, als wir nicht erwartet foo noch war der tatsächliche Wert bar!

Im Grunde ist dies wie ein SQL-Injektion Angriff, aber in dem eher weniger beängstigenden Kontext von string.Format.

Als Workaround können Sie string.Format verwenden, wie StriplingWarrior vorschlägt. Dies vermeidet, dass die zweite Formatierungsebene für das Ergebnis der Formatierung mit den tatsächlichen/erwarteten Werten ausgeführt wird.

+0

Danke für die ausführliche Antwort Jon! Ich habe am Ende mit StriplingWarriors gearbeitet. – Kyle

+1

Kein '% * n' Äquivalent? :( –

+0

Hat jemand einen Fehlerbericht dafür? – Kevin

3

Ich denke, die erste Behauptung ist falsch.

Verwenden Sie stattdessen:

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

Laut der Dokumentation sollte ich in der Lage sein, AreEqual mit einer formatierten Zeichenfolge aufzurufen. Http://msdn.microsoft.com/en-us/library/ms243436%28v=vs.100%29.aspx, speziell Parameter Typ: System.Object [] Ein Array von Parametern zum Formatieren von Nachrichten – Kyle

43

Ich glaube, Sie einen Fehler gefunden haben.

Dies funktioniert (eine Assertion Ausnahme auslöst):

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

Und das funktioniert (gibt die Meldung):

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

Aber das funktioniert nicht (wirft einen FormatException):

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

Ich kann mir keinen Grund denken, dies wäre das erwartete Verhalten. Ich würde einen Fehlerbericht einreichen. In der Zwischenzeit, hier eine Abhilfe:

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

ich mit @StriplingWarrior einig, dass dies in der Tat scheint ein Fehler mit dem Assert.AreEqual() -Methode auf mindestens 2 Überlastungen zu sein. Wie bereits von StiplingWarrior aufgezeigt, scheitert das Folgende;

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

Ich mache ein wenig auf das Experimentieren weiter auf ein wenig mehr explizit im Code Verwendung zu sein. Folgendes funktioniert auch nicht;

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

Und

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

Das hat mich zum Nachdenken. System.Drawing.Size ist eine Struktur. Was ist mit Objekten? Die Parameterliste gibt an, dass die Liste nach der string Nachricht params object[] ist. Technisch gesehen sind yes Strukturen Objekte ... aber spezielle Arten von Objekten, dh Werttypen. Ich denke, hier liegt der Fehler. Wenn wir unser eigenes Objekt mit einer ähnlichen Verwendung und Struktur wie Size verwenden, funktioniert das Folgende tatsächlich funktioniert;

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); 
} 
+1

Das Problem ist nicht, ob es 'class' oder' struct' ist, aber ob der 'ToString'-Wert geschweifte Klammern enthält, die wie ein' String aussehen .Format. –