Pourquoi cette affirmation jette-t-elle une exception de format lors de la comparaison de structures?

J’essaie d’affirmer l’égalité de deux structures System.Drawing.Size et j’obtiens une exception de format au lieu de l’échec d’assertion attendu.

 [TestMethod] public void AssertStructs() { var struct1 = new Size(0, 0); var struct2 = new Size(1, 1); //This throws a format exception, "System.FormatException: Input ssortingng 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); } 

Ce comportement est-il prévu? Est-ce que je fais quelque chose de mal ici?

J’ai compris. Et oui, c’est un bug.

Le problème est qu’il existe deux niveaux de ssortingng.Format .

Le premier niveau de formatage est quelque chose comme:

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

Ensuite, nous utilisons ssortingng.Format avec les parameters que vous avez fournis:

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

(Évidemment, il y a des cultures fournies et une sorte de désinfection … mais pas assez.)

Cela semble correct – à moins que les valeurs attendues et les valeurs réelles elles-mêmes ne se terminent par des accolades, après avoir été converties en chaîne – ce qu’elles font pour Size . Par exemple, votre première taille est convertie en:

 {Width=0, Height=0} 

Donc, le deuxième niveau de formatage est quelque chose comme:

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

… et c’est ce qui manque. Aie.

En effet, nous pouvons le prouver très facilement en trompant le formatage pour utiliser nos parameters pour les parties attendues et réelles:

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

Le résultat est:

 Assert.AreEqual failed. Expected:. Actual:. What a surprise! 

Clairement cassé, car nous ne nous attendions pas à foo ni la bar valeur réelle!

Fondamentalement, cela ressemble à une attaque par injection SQL, mais dans le contexte moins effrayant de ssortingng.Format .

Pour contourner ce ssortingng.Format , vous pouvez utiliser ssortingng.Format comme le suggère SsortingplingWarrior. Cela évite le deuxième niveau de formatage sur le résultat du formatage avec les valeurs réelles / attendues.

Je pense que vous avez trouvé un bug.

Cela fonctionne (lance une exception d’affirmation):

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

Et cela fonctionne (génère le message):

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

Mais cela ne fonctionne pas (lève une FormatException ):

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

Je ne peux penser à aucune raison pour laquelle ce comportement serait attendu. Je soumettrais un rapport de bogue. En attendant, voici une solution de contournement:

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

Je suis d’accord avec @SsortingplingWarrior que cela semble effectivement être un bug avec la méthode Assert.AreEqual () sur au moins 2 surcharges. Comme StiplingWarrior l’a déjà souligné, les échecs suivants

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

J’ai fait un peu plus d’expérimentations à ce sujet en étant un peu plus explicite dans l’utilisation du code. Ce qui suit ne fonctionne pas non plus;

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

Et

 // 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(a, b, "Not equal {0} {1}", a, b); 

Cela m’a fait réfléchir. System.Drawing.Size est une structure. Qu’en est-il des objects? La liste de params object[] spécifie que la liste après le message de ssortingng est params object[] . Techniquement, les structures oui sont des objects … mais des types spéciaux d’objects, c’est-à-dire des types de valeur. Je pense que c’est là que réside le bug. Si nous utilisons notre propre object avec une utilisation et une structure similaires à la Size , ce qui suit fonctionne réellement;

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

Je pense que la première affirmation est incorrecte.

Utilisez ceci à la place:

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