throwsexception expected example c# unit-testing exception-handling mstest string-formatting

c# - expected - assert.throwsexception example



¿Por qué esta afirmación arroja una excepción de formato al comparar estructuras? (4)

Creo que has encontrado un error.

Esto funciona (arroja una excepción afirmar):

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

Y esto funciona (muestra el mensaje):

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

Pero esto no funciona (arroja una FormatException ):

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

No puedo pensar en ninguna razón por la cual este sea el comportamiento esperado. Enviaría un informe de error. Mientras tanto, aquí hay una solución alternativa:

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

Estoy tratando de afirmar la igualdad de dos estructuras System.Drawing.Size , y obtengo una excepción de formato en lugar de la falla de afirmación esperada.

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

¿Es este comportamiento intencionado? ¿Estoy haciendo algo mal aquí?


Creo que la primera afirmación es incorrecta.

Use esto en su lugar:

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


Estoy de acuerdo con @StriplingWarrior en que parece ser un error con el método Assert.AreEqual () en al menos 2 sobrecargas. Como StiplingWarrior ya ha señalado, lo siguiente falla;

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

He estado experimentando un poco más sobre esto en ser un poco más explícito en el uso del código. Lo siguiente tampoco funciona;

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

Y

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

Esto me hizo pensar. System.Drawing.Size es una estructura. ¿Qué pasa con los objetos? La lista de parámetros especifica que la lista después del mensaje de string es el params object[] . Técnicamente, sí, las estructuras son objetos ... pero tipos especiales de objetos, es decir, tipos de valores. Creo que aquí es donde yace el error. Si usamos nuestro propio objeto con un uso y una estructura similar a Size , lo siguiente realmente funciona;

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


Lo tengo. Y sí, es un error.

El problema es que hay dos niveles de string.Format . El string.Format continúa aquí.

El primer nivel de formateo es algo así como:

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

Luego usamos string.Format con los parámetros que ha proporcionado:

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

(Obviamente hay culturas que se proporcionan, y algún tipo de desinfección ... pero no lo suficiente).

Eso se ve bien, a menos que los valores esperados y reales terminen con llaves, después de haber sido convertidos en una cadena, lo cual hacen por Size . Por ejemplo, su primer tamaño termina convirtiéndose en:

{Width=0, Height=0}

Entonces, el segundo nivel de formateo es algo así como:

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

... y eso es lo que está fallando. Ay.

De hecho, podemos probar esto muy fácilmente al engañar el formato para usar nuestros parámetros para las partes esperadas y reales:

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

El resultado es:

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

Claramente roto, ya que no esperábamos foo ni era la bar valor real!

Básicamente, esto es como un ataque de inyección SQL, pero en el contexto bastante menos aterrador de string.Format .

Como solución alternativa, puede utilizar string.Format como sugiere StriplingWarrior. Eso evita que el segundo nivel de formateo se realice sobre el resultado del formateo con los valores reales / esperados.