2008-09-22 17 views
38

Visual Studio Test puede verificar excepciones esperadas utilizando el atributo ExpectedException. Puede pasar de una excepción de esta manera:¿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?

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

También puede comprobar si el mensaje contenido en el ExpectedException así:

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

Pero al probar aplicaciones I18N me gustaría utilizar un archivo de recursos para obtener ese mensaje de error (cualquier puede incluso decidir poner a prueba las diferentes localizaciones del mensaje de error si quiero, pero Visual Studio no me deja hacer esto:

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

la compil er dará el siguiente error:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute

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


Una de las opciones que he considerado es el uso de clases de excepción personalizada, pero basado en el asesoramiento a menudo se escucha como:

"Do create and throw custom exceptions if you have an error condition that can be programmatically handled in a different way than any other existing exception. Otherwise, throw one of the existing exceptions." Source

No estoy esperando para manejar las excepciones de manera diferente en el flujo normal (que es una excepción crítica, así que entraré en 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?

+3

El parámetro mensaje no verifica contra el mensaje de la excepción lanzada. –

Respuesta

7

Sólo una opinión, pero yo diría que el texto de error:

  • es parte de la prueba, en cuyo caso la reciba de los recursos sería 'equivocado' (de lo contrario podría terminar con una consistencia recurso destruido), entonces 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 dejar de probar varios idiomas, dada la capacidad de funcionar 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 .Net's el sistema de metadatos probablemente no le gusta, de ahí ese consejo.

+0

Implementaría un proceso simple que lea el archivo de recursos y encuentre el mensaje que se escribirá, su método es una excelente manera de quitar el mapa y perder valioso tiempo actualizando las pruebas (lo que estamos intentando evitar) –

+0

@Mickey: Well entonces, estás de suerte: ¡la otra respuesta es lo que estás buscando! Si * específicamente * quiere una ** unidad ** - prueba que recupera el mismo mensaje, está bien, pero tiendo a preferir que recupere el mensaje * correcto *. ¡Pero si su proyecto es diferente, entonces puede tener una respuesta "correcta" diferente! –

+0

No sigo, usando las pruebas de la unidad DataBound, puedes guardar tu pastel y comértelo también. mapear los recursos en el enlace de datos y mapear la cultura de su mensaje en la prueba, o viceversa. no ? –

4

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 fue una excepción, entonces el caso de prueba debe considerarse como un fracaso)

3

Si cambia hacia el uso de la biblioteca muy agradable xUnit.Net prueba, 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. 
} 
1

Me pregunto si NUnit se está moviendo por el camino de la simplicidad ... pero aquí tienes.

Las nuevas mejoras (2.4.3 y más?) Al atributo ExpectedException le permiten tener más control sobre las comprobaciones que se realizarán en la Excepción esperada a través del método del Manejador. Más detalles sobre el official NUnit doc page .. 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 internacionalizados excepciones .. ¿Está utilizando excepciones para las cosas que necesitan ser manipulados o notificado 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 tenga que tener esto ... es posible :)

+0

¿No cree que el método para obtener el mensaje de diálogo de error de la excepción throwen es un buen diseño? Tenía la impresión de que era común, y simplifica el código de ruta de error ... ¡Sé que no me gustaría ver un diálogo en chino! –

+0

Estoy usando NUnit framework versión 2.6 y el atributo [ExpectedException (Handler = "HandlerMethod")] no compila – dotnetguy

+0

@mishrsud - Creo que el compilador le dará más detalles sobre eso. Verifique el panel de salida, ¿de qué se queja? – Gishu

63

Recomendaría utilizar 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 usted puede escribir algo a su prueba como esta:

[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 la ventaja de que verifica que la excepción se produce en la línea que se esperaba que fuera lanzado en lugar de cualquier lugar en su método de prueba.

+4

¡¡¡manera de ir !!! Me gusta de esta manera más. – argatxa

+1

¡Qué respuesta! Increíble. Me ayudó a mí también :) – Learner

+0

Gran solución que funcionó para mí. –

13

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.

0

Me encontré con esta pregunta al tratar de resolver un problema similar por mi cuenta. (Voy a 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 coherencia 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 punto, consideré (y probé) el uso de bloques try/catch para evitar el requisito de una constante por el atributo ExpectedException, pero parecía que esto generaría bastante código adicional si se aplicaba en un 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 encaja con mi deseo de no hacer la verificación del texto de la excepción de un proceso complejo.)

Mi código de prueba a continuación, simplemente se reduce a (perdón por el mangling ...):

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