vscode visual tag studio code beautiful visual-studio unit-testing exception internationalization

tag - ¿Cómo puedo probar una excepción esperada con un mensaje de excepción específico de un archivo de recursos en Visual Studio Test?



vs code edit html tags (7)

Visual Studio Test puede verificar excepciones esperadas utilizando el atributo ExpectedException. Puede pasar una excepción como esta:

[TestMethod] [ExpectedException(typeof(CriticalException))] public void GetOrganisation_MultipleOrganisations_ThrowsException()

También puede verificar el mensaje contenido en ExpectedException de la siguiente manera:

[TestMethod] [ExpectedException(typeof(CriticalException), "An error occured")] public void GetOrganisation_MultipleOrganisations_ThrowsException()

Pero al probar las aplicaciones I18N usaría un archivo de recursos para obtener ese mensaje de error (cualquiera incluso puede decidir probar las diferentes localizaciones del mensaje de error si quiero, pero Visual Studio no me dejará hacer esto:

[TestMethod] [ExpectedException(typeof(CriticalException), MyRes.MultipleOrganisationsNotAllowed)] public void GetOrganisation_MultipleOrganisations_ThrowsException()

El compilador dará el siguiente error:

Un argumento de atributo debe ser una expresión constante, tipo de expresión o expresión de creación de matriz de un atributo

¿Alguien sabe cómo probar una excepción que tiene un mensaje de un archivo de recursos?

Una opción que he considerado es el uso de clases de excepciones personalizadas, pero basadas en consejos frecuentes como:

"Cree y ejecute excepciones personalizadas si tiene una condición de error que puede manejarse mediante programación de una manera diferente a cualquier otra excepción existente. De lo contrario, ejecute una de las excepciones existentes". Source

No espero manejar las excepciones de forma diferente en el flujo normal (es una excepción crítica, así que entraré en el modo de pánico de todos modos) y no creo que crear una excepción para cada caso de prueba sea lo correcto. Alguna opinion?


Creo que puede hacer un try-catch explícito en su código de prueba en lugar de confiar en el atributo ExpectedException para hacerlo por usted. Luego puede encontrar algún método auxiliar que lea el archivo de recursos y compare el mensaje de error con el que viene con la excepción que fue capturada. (por supuesto, si no hubiera una excepción, el caso de prueba debería considerarse un fracaso)


El argumento ExpectedException Message no coincide con el mensaje de la excepción. Más bien, este es el mensaje que se imprime en los resultados de la prueba si la excepción esperada no se produjo de hecho.


Me encontré con esta pregunta al tratar de resolver un problema similar por mi cuenta. (Detallaré la solución que establecí a continuación).

Tengo que estar de acuerdo con los comentarios de Gishu sobre la internacionalización de los mensajes de excepción que son un olor a código.

Lo había hecho inicialmente en mi propio proyecto para que pudiera tener consistencia entre los mensajes de error arrojados por mi aplicación y en mis pruebas unitarias. es decir, para tener que definir mis mensajes de excepción en un solo lugar y en ese momento, el archivo de Recursos parecía un lugar sensato para hacerlo, porque ya lo estaba usando para varias etiquetas y cadenas (y dado que tenía sentido agregar una referencia) a él en mi código de prueba para verificar que esas mismas etiquetas se muestran en los lugares apropiados).

En un momento dado, consideré (y probé) el uso de bloques de prueba / captura para evitar el requisito de una constante por el atributo ExpectedException, pero parecía que llevaría a una gran cantidad de código adicional si se aplicaba a gran escala.

Al final, la solución que establecí fue crear una clase estática en mi biblioteca de recursos y almacenar mis mensajes de excepción en eso. De esta forma no hay necesidad de internacionalizarlos (lo que estoy de acuerdo no tiene sentido) y están disponibles en cualquier momento que se pueda acceder a una cadena de recursos ya que están en el mismo espacio de nombres. (Esto se ajusta a mi deseo de no hacer que la verificación del texto de excepción sea un proceso complejo).

Mi código de prueba simplemente se reduce a (perdón por los tramposos ...):

[Test, ExpectedException(typeof(System.ArgumentException), ExpectedException=ProductExceptionMessages.DuplicateProductName)] public void TestCreateDuplicateProduct() { _repository.CreateProduct("TestCreateDuplicateProduct"); _repository.CreateProduct("TestCreateDuplicateProduct"); }


Me pregunto si NUnit se está alejando de la simplicidad ... pero aquí tienes.

Las nuevas mejoras (2.4.3 y superiores?) Al atributo ExpectedException le permiten un mayor control sobre las comprobaciones que se realizarán en la Excepción esperada mediante un método Handler . Más detalles en la página oficial de documentos de NUnit ... hacia el final de la página.

[ExpectedException( Handler="HandlerMethod" )] public void TestMethod() { ... } public void HandlerMethod( System.Exception ex ) { ... }

Nota: Algo no se siente bien aquí ... ¿Por qué sus mensajes de excepción están internacionalizados? ¿Está utilizando excepciones para cosas que deben manejarse o notificarse al usuario? A menos que tengas un grupo de desarrolladores culturalmente diversos que corrijan errores ... no deberías necesitar esto. Las excepciones en inglés o un lenguaje común aceptado serían suficientes. Pero en caso de que tengas que tener esto ... es posible :)


Si cambia a usar la muy bonita biblioteca de pruebas xUnit.Net , puede reemplazar [ExpectedException] con algo como esto:

[Fact] public void TestException() { Exception ex = Record.Exception(() => myClass.DoSomethingExceptional()); // Assert whatever you like about the exception here. }


Yo recomendaría usar un método de ayuda en lugar de un atributo. Algo como esto:

public static class ExceptionAssert { public static T Throws<T>(Action action) where T : Exception { try { action(); } catch (T ex) { return ex; } Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); // The compiler doesn''t know that Assert.Fail // will always throw an exception return null; } }

Entonces puedes escribir tu prueba de la siguiente manera:

[TestMethod] public void GetOrganisation_MultipleOrganisations_ThrowsException() { OrganizationList organizations = new Organizations(); organizations.Add(new Organization()); organizations.Add(new Organization()); var ex = ExceptionAssert.Throws<CriticalException>( () => organizations.GetOrganization()); Assert.AreEqual(MyRes.MultipleOrganisationsNotAllowed, ex.Message); }

Esto también tiene el beneficio de que verifica que la excepción se arroje en la línea que esperaba que se lanzara en lugar de en cualquier lugar en su método de prueba.


Solo una opinión, pero diría el texto de error:

  • es parte de la prueba, en cuyo caso obtenerlo del recurso sería "incorrecto" (de lo contrario, podría terminar con un recurso constantemente destrozado), así que simplemente actualice la prueba cuando cambie el recurso (o la prueba falla)
  • no es parte de la prueba, y solo debería preocuparse de que arroje la excepción.

Tenga en cuenta que la primera opción debe permitirle probar varios idiomas, dada la posibilidad de ejecutar con una configuración regional.

En cuanto a las excepciones múltiples, soy de C ++ Land, donde crear cargas y montones de excepciones (¡hasta el punto de una por declaración ''throw''!) En grandes heirachies es aceptable (si no es común), pero el sistema de metadatos de .Net probablemente no Me gusta eso, de ahí ese consejo.