2009-12-22 21 views
30

Usando MSTest ¿cómo puedo verificar el mensaje de error exacto proveniente de un método de prueba? Sé que [ExpectedException(typeof(ApplicationException), error msg)] no compara el mensaje de error proveniente de mi método de prueba, aunque en otro marco de prueba de unidad lo está haciendo.En MSTest, ¿cómo puedo verificar el mensaje de error exacto usando [ExpectedException (typeof (ApplicationException))]

Una forma de resolver este problema es escribir mi prueba unitaria usando algún bloque try catch, pero de nuevo necesito escribir 4 líneas más.

¿Existe alguna forma más inteligente de verificar el mensaje de error?

Saludos, Pritam

+0

título fijo. No sabía que MSTest no tenía esto ... debería haber sido simple para admitir – Gishu

Respuesta

0

Con MSTest, que no pueden hacer esto.

Ya conoce la solución a este problema: afirme el mensaje de una excepción en un bloque catch.

-1

Actualización: Ups ... vea que quiere esto en MSTest. Lo siento. Velocidad de lectura & Engañado por su título.

Pruebe este proyecto de extensión de Callum Hibbert y vea si funciona.

respuesta antigua:

Usted puede hacer esto con NUnit 2.4 y superiores. Ver documentation de ExpectedException aquí

[ExpectedException(typeof(ArgumentException), ExpectedMessage="unspecified", MatchType=MessageMatch.Contains)] 
public void TestMethod() 
{ 
... 

MatchType puede ser exacta (por defecto), contiene o expresiones regulares .. que maneja más o menos el caso de uso 80%. También existe un método avanzado de manejo de excepciones si la verificación se vuelve demasiado compleja ... nunca la usé personalmente ... aún no la necesitaba.

0

MbUnit también puede hacer esto:

[Test] 
[Row(ExpectedExceptionMessage="my message")] 
void TestBlah(... 
4

En MSTest no hay forma integrada de hacerlo. Esto es lo más 'elegante' como se pone:

[TestMethod] 
public void Test8() 
{ 
    var t = new Thrower(); 
    try 
    { 
     t.DoStuffThatThrows(); 
     Assert.Fail("Exception expected."); 
    } 
    catch (InvalidOperationException e) 
    { 
     Assert.AreEqual("Boo hiss!", e.Message); 
    } 
} 

Sin embargo, usted podría considerar portar API Assert.Throws de xUnit.NET a una biblioteca personalizada - eso es lo que hicimos.

También puede ir a Microsoft Connect un vote on this suggestion.

15

Uso esta pequeña clase de ayuda:

public static class ExceptionAssert 
{ 
    public static void Throws<TException>(Action action, string message) 
     where TException : Exception 
    { 
     try 
     { 
      action(); 

      Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name); 
     } 
     catch (TException ex) 
     { 
      Assert.AreEqual(message, ex.Message); 
     } 
     catch (Exception ex) 
     { 
      Assert.Fail("Exception of type {0} expected; got exception of type {1}", typeof(TException).Name, ex.GetType().Name);    
     } 
    } 
} 

Uso:

Foo foo = new Foo(); 
foo.Property = 42; 

ExceptionAssert.Throws<InvalidOperationException>(() => foo.DoSomethingCritical(), "You cannot do anything when Property is 42."); 

La ventaja de excepciones captura explícitas es que teh prueba no tiene éxito cuando otro miembro (por ejemplo, durante la inicialización) lanza la excepción.

+0

¡Agradable! Usaré su método y lo extenderé para afirmar ** InnerException **: D –

+0

¿No debería estar el primer Assert.Fail al final, fuera del bloque try/catch? Si lo pones está dentro del bloque try, entonces será atrapado por el segundo bloque catch. Lo cual causa dos excepciones. – Rhyous

+0

@Rhyous La solución al Assert.Fail es agregar lo siguiente como el primer bloque de captura catch (AssertFailedException) {throw; } –

4

Fluent Assertions (disponible en NuGet) tiene una sintaxis muy "lenguaje natural" para definir las expectativas en las pruebas de unidad:

objectundertest.Invoking(o => o.MethodUnderTest()).ShouldThrow<ExpectedException>() 
    .WithMessage("the expected error message"); 

Existen múltiples variaciones para comprobar el mensaje de error con cualquier algoritmo (Where(e => ...), así como revisando las excepciones internas y sus mensajes.

5

Estaba buscando una forma de verificar la presencia y el tipo de excepción interna con mstest y encontré esta pregunta. Sabía que era un tema de hace dos años, pero como mi solución no está aquí, déjame compartirla.

Para mí, la forma más elegante de resolver el problema es crear un atributo derivado, aquí es mío (lo siento pero los comentarios y las cadenas están en mi lenguaje natural francés, pero debería ser obvio):

#region Références 
using System; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Text.RegularExpressions; 
#endregion 

namespace MsTestEx 
{ 
    /// <summary> 
    /// Extention de l'attribut ExpectedException permettant de vérifier plus d'éléments (Message, InnerException, ...) 
    /// </summary> 
    public class ExpectedExceptionEx 
     : ExpectedExceptionBaseAttribute 
    { 
     #region Variables locales 
     private Type _ExpectedException    = null; 
     private string _ExpectedMessage    = null; 
     private Type _ExpectedInnerException   = null; 
     private string _ExpectedInnerExceptionMessage = null; 
     private bool _IsExpectedMessageRegex   = false; 
     private bool _IsExpectedInnerMessageRegex = false; 
     private bool _AllowDerivedType    = false; 
     private bool _AllowInnerExceptionDerivedType = false; 

     private bool _CheckExpectedMessage   = false; 
     private bool _CheckInnerExceptionType  = false; 
     private bool _CheckInnerExceptionMessage  = false; 
     #endregion 

     #region Propriétés 
     /// <summary> 
     /// Vérifie que le message de l'exception correspond à celui-ci. 
     /// </summary> 
     public string ExpectedMessage 
     { 
      get { return _ExpectedMessage; } 
      set { _ExpectedMessage = value; _CheckExpectedMessage = true; } 
     } 

     /// <summary> 
     /// Vérifie que le message de l'inner-exception correspond à celui-ci. 
     /// </summary> 
     public string ExpectedInnerExceptionMessage 
     { 
      get { return _ExpectedInnerExceptionMessage; } 
      set { _ExpectedInnerExceptionMessage = value; _CheckInnerExceptionMessage = true; } 
     } 

     /// <summary> 
     /// Vérifie que l'exception possède bien une inner-exception du type spécifié. 
     /// Spécifier "null" pour vérifier l'absence d'inner-exception. 
     /// </summary> 
     public Type ExpectedInnerException 
     { 
      get { return _ExpectedInnerException; } 
      set { _ExpectedInnerException = value; _CheckInnerExceptionType = true; } 
     } 

     /// <summary> 
     /// Indique si le message attendu est exprimé via une expression rationnelle. 
     /// </summary> 
     public bool IsExpectedMessageRegex 
     { 
      get { return _IsExpectedMessageRegex; } 
      set { _IsExpectedMessageRegex = value; } 
     } 

     /// <summary> 
     /// Indique si le message attendu de l'inner-exception est exprimé via une expression rationnelle. 
     /// </summary> 
     public bool IsExpectedInnerMessageRegex 
     { 
      get { return _IsExpectedInnerMessageRegex; } 
      set { _IsExpectedInnerMessageRegex = value; } 
     } 

     /// <summary> 
     /// Indique si les exceptions dérivées sont acceptées. 
     /// </summary> 
     public bool AllowDerivedType 
     { 
      get { return _AllowDerivedType; } 
      set { _AllowDerivedType = value; } 
     } 

     /// <summary> 
     /// Indique si les inner-exceptions dérivées sont acceptées. 
     /// </summary> 
     public bool AllowInnerExceptionDerivedType 
     { 
      get { return _AllowInnerExceptionDerivedType; } 
      set { _AllowInnerExceptionDerivedType = value; } 
     } 
     #endregion 

     #region Constructeurs 
     /// <summary> 
     /// Indique le type d'exception attendu par le test. 
     /// </summary> 
     /// <param name="expectedException">Type de l'exception attendu.</param> 
     public ExpectedExceptionEx(Type expectedException) 
     { 
      _ExpectedException = expectedException; 
     } 
     #endregion 

     #region Méthodes 
     /// <summary> 
     /// Effectue la vérification. 
     /// </summary> 
     /// <param name="exception">Exception levée.</param> 
     protected override void Verify(Exception exception) 
     { 
      Assert.IsNotNull(exception); // Pas eu d'exception, ce n'est pas normal 

      // Vérification du type de l'exception 
      Type actualType = exception.GetType(); 
      if (_AllowDerivedType) Assert.IsTrue(_ExpectedException.IsAssignableFrom(actualType), "L'exception reçue n'est pas du type spécifié ni d'un type dérivé."); 
      else Assert.AreEqual(_ExpectedException, actualType, "L'exception reçue n'est pas du type spécifié."); 

      // Vérification du message de l'exception 
      if (_CheckExpectedMessage) 
      { 
       if (_IsExpectedMessageRegex) 
        Assert.IsTrue(Regex.IsMatch(exception.Message, _ExpectedMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle"); 
       else 
       { 
        string s1, s2; 
        if (exception.Message.Length > _ExpectedMessage.Length) 
        { 
         s1 = exception.Message; 
         s2 = _ExpectedMessage; 
        } 
        else 
        { 
         s1 = _ExpectedMessage; 
         s2 = exception.Message; 
        } 
        Assert.IsTrue(s1.Contains(s2), "Le message de l'exception ne contient pas et n'est pas contenu par le message attendu."); 
       } 
      } 

      if (_CheckInnerExceptionType) 
      { 
       if (_ExpectedInnerException == null) Assert.IsNotNull(exception.InnerException); 
       else 
       { 
        // Vérification du type de l'exception 
        actualType = exception.InnerException.GetType(); 
        if (_AllowInnerExceptionDerivedType) Assert.IsTrue(_ExpectedInnerException.IsAssignableFrom(actualType), "L'inner-exception reçue n'est pas du type spécifié ni d'un type dérivé."); 
        else Assert.AreEqual(_ExpectedInnerException, actualType, "L'inner-exception reçue n'est pas du type spécifié."); 
       } 
      } 

      if (_CheckInnerExceptionMessage) 
      { 
       Assert.IsNotNull(exception.InnerException); 
       if (_IsExpectedInnerMessageRegex) 
        Assert.IsTrue(Regex.IsMatch(exception.InnerException.Message, _ExpectedInnerExceptionMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle"); 
       else 
       { 
        string s1, s2; 
        if (exception.InnerException.Message.Length > _ExpectedInnerExceptionMessage.Length) 
        { 
         s1 = exception.InnerException.Message; 
         s2 = _ExpectedInnerExceptionMessage; 
        } 
        else 
        { 
         s1 = _ExpectedInnerExceptionMessage; 
         s2 = exception.InnerException.Message; 
        } 
        Assert.IsTrue(s1.Contains(s2), "Le message de l'inner-exception ne contient pas et n'est pas contenu par le message attendu."); 
       } 
      } 
     } 
     #endregion 
    } 
} 

Ahora, use este atributo con parámetros nombrados en lugar de "ExpectedException". Con mi atributo puede verificar si hay una excepción interna, un mensaje de la excepción y una excepción interna, use una expresión regular para hacer coincidir los mensajes, etc. ... Puede adaptarlo como lo desee.

+0

Gracias por compartir :) – Anne

31

Puede create your own ExpectedException attribute donde puede Assert el mensaje del Exception que se arrojó.

Código

namespace TestProject 
{ 
    public sealed class MyExpectedException : ExpectedExceptionBaseAttribute 
    { 
     private Type _expectedExceptionType; 
     private string _expectedExceptionMessage; 

     public MyExpectedException(Type expectedExceptionType) 
     { 
      _expectedExceptionType = expectedExceptionType; 
      _expectedExceptionMessage = string.Empty; 
     } 

     public MyExpectedException(Type expectedExceptionType, string expectedExceptionMessage) 
     { 
      _expectedExceptionType = expectedExceptionType; 
      _expectedExceptionMessage = expectedExceptionMessage; 
     } 

     protected override void Verify(Exception exception) 
     { 
      Assert.IsNotNull(exception); 

      Assert.IsInstanceOfType(exception, _expectedExceptionType, "Wrong type of exception was thrown."); 

      if(!_expectedExceptionMessage.Length.Equals(0)) 
      { 
       Assert.AreEqual(_expectedExceptionMessage, exception.Message, "Wrong exception message was returned."); 
      } 
     } 
    } 
} 

Uso

[TestMethod] 
[MyExpectedException(typeof(Exception), "Error")] 
public void TestMethod() 
{ 
    throw new Exception("Error"); 
} 
+1

Me gusta ... realmente es una buena manera de verificar si hay mensajes de excepción personalizados. :) –

0

La molestia con anotaciones y bloques try/catch es que usted no tiene una clara separación entre la ACT y hacer valer las fases de la prueba. Un appraoch más simple es el de "capturar" la excepción como parte de la fase de ACT usando una rutina utitlity tales como:

public static class Catch 
{ 
    public static Exception Exception(Action action) 
    { 
     Exception exception = null; 

     try 
     { 
      action(); 
     } 
     catch (Exception ex) 
     { 
      exception = ex; 
     } 

     return exception; 
    } 
} 

Esto le permite hacer:

// ACT 
var actualException = Catch.Exception(() => DoSomething()) 

// ASSERT 
Assert.IsNotNull(actualException, "No exception thrown"); 
Assert.IsInstanceOfType(actualException, expectedType); 
Assert.AreEqual(expectedExceptionMessage, actualException.Message); 
Cuestiones relacionadas