2011-01-17 16 views
26

Pensé que estas dos pruebas deberían comportarse de manera idéntica, de hecho, escribí la prueba en mi proyecto usando MS Test solo para descubrir ahora que no respeta el mensaje esperado en el mismo forma en que NUnit lo hace.Cómo implico el mensaje de excepción con el atributo ExpectedException

NUnit (falla):

[Test, ExpectedException(typeof(System.FormatException), ExpectedMessage = "blah")] 
public void Validate() 
{ 
    int.Parse("dfd"); 
} 

MS Test (pasa):

[TestMethod, ExpectedException(typeof(System.FormatException), "blah")] 
public void Validate() 
{ 
    int.Parse("dfd"); 
} 

No importa cuál es el mensaje que doy la prueba ms, pasará.

¿Hay alguna forma de que la prueba ms falle si el mensaje no es correcto? ¿Puedo incluso crear mi propio atributo de excepción? Preferiría no tener que escribir un bloque try catch para cada prueba donde esto ocurra.

Respuesta

21

Ese segundo parámetro mstest es un mensaje que se imprime cuando falla la prueba. La mstest tendrá éxito si se genera una formatexception. Me encontré con este post que puede ser útil

http://blogs.msdn.com/b/csell/archive/2006/01/13/expectedexception-might-not-be-what-you-ve-expected.aspx

+2

Gracias, eso es una gran información. ¿Conoces una forma de usar ms test para escribir una prueba que se comporte igual que la nunit? – CRice

+1

Podría considerar la creación de un atributo personalizado como nunit. – CRice

4

Puede utilizar el código de this project se explica en this article para crear un atributo ExpectedExceptionMessage que funcionará con MS prueba para obtener el resultado deseado.

prueba ms (falla):

[TestClass] 
public class Tests : MsTestExtensionsTestFixture 
{ 
    [TestMethod, ExpectedExceptionMessage(typeof(System.FormatException), "blah")] 
    public void Validate() 
    { 
     int.Parse("dfd"); 
    } 
} 
8

@rcravens es correcto - el segundo parámetro es un mensaje que se imprime si no pasa la prueba. Lo que he hecho para solucionar esto es crear mis pruebas de forma un poco diferente. Es cierto que no me gusta este enfoque, pero funciona.

[TestMethod] 
public void Validate() 
{ 
    try 
    { 
     int.Parse("dfd"); 

     // Test fails if it makes it this far 
     Assert.Fail("Expected exception was not thrown."); 
    } 
    catch (Exception ex) 
    { 
     Assert.AreEqual("blah", ex.Message); 
    } 
} 
+0

Soy un fan de este enfoque ahora si uso mstest – CRice

+0

Gracias, esto hará rápidamente lo que quiero que haga, valide mi mensaje –

+0

Esto es en realidad una solución muy astuta .. ¡Me encanta! – ppumkin

0

Probar esto. En este ejemplo, tengo un mensaje de saludo que muestra el mensaje de saludo para un nombre de pila. Cuando el parámetro de nombre está vacío o el sistema nulo arroja una excepción con un mensaje. El ExceptionAssert.Throws verifica ambos en MsTest.

[TestMethod] 
public void Does_Application_Display_Correct_Exception_Message_For_Empty_String() 
{ 
    // Arrange 
    var oHelloWorld = new HelloWorld(); 

    // Act 

    // Asset 

    ExceptionAssert.Throws<ArgumentException>(() => 
      oHelloWorld.GreetingMessge(""),"Invalid Name, Name can't be empty"); 
} 

[TestMethod] 
public void Does_Application_Display_Correct_Exception_Message_For_Null_String() 
{ 
    // Arrange 
    var oHelloWorld = new HelloWorld(); 

    // Act 

    // Asset 

    ExceptionAssert.Throws<ArgumentNullException>(() => 
     oHelloWorld.GreetingMessge(null), "Invalid Name, Name can't be null"); 
} 
+2

ExceptionAssert no parece estar incluido en Microsoft UnitTesting, ¿está utilizando un complemento de terceros? – ForceMagic

23

Utilizamos este atributo por todo el lugar y mal entendido con claridad el segundo parámetro (que pena por nosotros).

Sin embargo, definitivamente lo hemos usado para verificar el mensaje de excepción. Lo siguiente fue lo que usamos con sugerencias de esta página. No maneja la globalización ni los tipos de excepción heredados, pero hace lo que necesitamos. Una vez más, el objetivo era simplemente RR 'ExpectedException' y cambiarlo con esta clase. (Bummer ExpectedException está sellado.)

public class ExpectedExceptionWithMessageAttribute : ExpectedExceptionBaseAttribute 
{ 
    public Type ExceptionType { get; set; } 

    public string ExpectedMessage { get; set; } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType) 
    { 
     this.ExceptionType = exceptionType; 
    } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType, string expectedMessage) 
    { 
     this.ExceptionType = exceptionType; 

     this.ExpectedMessage = expectedMessage; 
    } 

    protected override void Verify(Exception e) 
    { 
     if (e.GetType() != this.ExceptionType) 
     { 
      Assert.Fail(String.Format(
          "ExpectedExceptionWithMessageAttribute failed. Expected exception type: {0}. Actual exception type: {1}. Exception message: {2}", 
          this.ExceptionType.FullName, 
          e.GetType().FullName, 
          e.Message 
          ) 
         ); 
     } 

     var actualMessage = e.Message.Trim(); 

     if (this.ExpectedMessage != null) 
     { 
      Assert.AreEqual(this.ExpectedMessage, actualMessage); 
     } 

     Console.Write("ExpectedExceptionWithMessageAttribute:" + e.Message); 
    } 
} 
+0

Traté de lograr lo mismo usando PostSharp, ¡pero heredar el atributo existente tiene mucho más sentido! –

+0

Literalmente hice esta clase yo mismo. Aunque lo llamé 'PrintExpectedExceptionAttribute' :) –

+0

¡Agradable! Como dicen, "las grandes mentes piensan igual y hacen exactamente el mismo trabajo en paralelo". Debo decir que usamos este atributo todo el tiempo. – BlackjacketMack

6

extendí respuesta de BlackjacketMack para nuestro proyecto mediante la adición de soporte para contiene, entre mayúsculas y minúsculas y combinaciones ResourcesType-resourceName.

Ejemplo de uso:

public class Foo 
{ 
    public void Bar(string mode) 
    { 
     if (string.IsNullOrEmpty(mode)) throw new InvalidOperationException(Resources.InvalidModeSpecified); 
    } 

    public void Bar(int port) 
    { 
     if (port < 0 || port > Int16.MaxValue) throw new ArgumentOutOfRangeException("port"); 
    } 
} 

[TestClass] 
class ExampleClass 
{ 
    [TestMethod] 
    [ExpectedExceptionWithMessage(typeof(InvalidOperationException), typeof(Samples.Resources), "InvalidModeSpecified")] 
    public void Raise_Exception_With_Message_Resource() 
    { 
     new Foo().Bar(null); 
    } 

    [TestMethod] 
    [ExpectedExceptionWithMessage(typeof(ArgumentOutOfRangeException), "port", true)] 
    public void Raise_Exeception_Containing_String() 
    { 
     new Foo().Bar(-123); 
    } 
} 

Esta es la clase de actualización:

public class ExpectedExceptionWithMessageAttribute : ExpectedExceptionBaseAttribute 
{ 
    public Type ExceptionType { get; set; } 

    public Type ResourcesType { get; set; } 

    public string ResourceName { get; set; } 

    public string ExpectedMessage { get; set; } 

    public bool Containing { get; set; } 

    public bool IgnoreCase { get; set; } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType) 
     : this(exceptionType, null) 
    { 
    } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType, string expectedMessage) 
     : this(exceptionType, expectedMessage, false) 
    { 
    } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType, string expectedMessage, bool containing) 
    { 
     this.ExceptionType = exceptionType; 
     this.ExpectedMessage = expectedMessage; 
     this.Containing = containing; 
    } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType, Type resourcesType, string resourceName) 
     : this(exceptionType, resourcesType, resourceName, false) 
    { 
    } 

    public ExpectedExceptionWithMessageAttribute(Type exceptionType, Type resourcesType, string resourceName, bool containing) 
    { 
     this.ExceptionType = exceptionType; 
     this.ExpectedMessage = ExpectedMessage; 
     this.ResourcesType = resourcesType; 
     this.ResourceName = resourceName; 
     this.Containing = containing; 
    } 

    protected override void Verify(Exception e) 
    { 
     if (e.GetType() != this.ExceptionType) 
     { 
      Assert.Fail(String.Format(
          "ExpectedExceptionWithMessageAttribute failed. Expected exception type: <{0}>. Actual exception type: <{1}>. Exception message: <{2}>", 
          this.ExceptionType.FullName, 
          e.GetType().FullName, 
          e.Message 
          ) 
         ); 
     } 

     var actualMessage = e.Message.Trim(); 

     var expectedMessage = this.ExpectedMessage; 

     if (expectedMessage == null) 
     { 
      if (this.ResourcesType != null && this.ResourceName != null) 
      { 
       PropertyInfo resourceProperty = this.ResourcesType.GetProperty(this.ResourceName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); 
       if (resourceProperty != null) 
       { 
        string resourceValue = null; 

        try 
        { 
         resourceValue = resourceProperty.GetMethod.Invoke(null, null) as string; 
        } 
        finally 
        { 
         if (resourceValue != null) 
         { 
          expectedMessage = resourceValue; 
         } 
         else 
         { 
          Assert.Fail("ExpectedExceptionWithMessageAttribute failed. Could not get resource value. ResourceName: <{0}> ResourcesType<{1}>.", 
          this.ResourceName, 
          this.ResourcesType.FullName); 
         } 
        } 
       } 
       else 
       { 
        Assert.Fail("ExpectedExceptionWithMessageAttribute failed. Could not find static resource property on resources type. ResourceName: <{0}> ResourcesType<{1}>.", 
         this.ResourceName, 
         this.ResourcesType.FullName); 
       } 
      } 
      else 
      { 
       Assert.Fail("ExpectedExceptionWithMessageAttribute failed. Both ResourcesType and ResourceName must be specified."); 
      } 
     } 

     if (expectedMessage != null) 
     { 
      StringComparison stringComparison = this.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; 
      if (this.Containing) 
      { 
       if (actualMessage == null || actualMessage.IndexOf(expectedMessage, stringComparison) == -1) 
       { 
        Assert.Fail(String.Format(
            "ExpectedExceptionWithMessageAttribute failed. Expected message: <{0}>. Actual message: <{1}>. Exception type: <{2}>", 
            expectedMessage, 
            e.Message, 
            e.GetType().FullName 
            ) 
           ); 
       } 
      } 
      else 
      { 
       if (!string.Equals(expectedMessage, actualMessage, stringComparison)) 
       { 
        Assert.Fail(String.Format(
            "ExpectedExceptionWithMessageAttribute failed. Expected message to contain: <{0}>. Actual message: <{1}>. Exception type: <{2}>", 
            expectedMessage, 
            e.Message, 
            e.GetType().FullName 
            ) 
           ); 
       } 
      } 
     } 
    } 
} 
Cuestiones relacionadas