Pregunta

Tengo varias pruebas de unidad de este patrón:

[TestMethod ()]
[ExpectedException (typeof (ArgumentNullException))]
public void DoStuffTest_Exception ()
{
    var foo = new Foo ();
    Foo.DoStuff (null);
}

Resulta que la cobertura del código marca la línea de lanzamiento como media carrera, por lo que obtengo 1 bloque de código descubierto cada vez.

Después de pensar en este problema por un tiempo, la mejor solución que se me ocurrió fue agregar un try / catch. Dado que este es un patrón repetido, voy a crear un método de ayuda en la línea de

public static void ExpectException<_T> (Action action) where _T: Exception
{
    try { action(); }
    catch (_T) { return; }
    Assert.Fail ("Expected " + _T);
}

Esto tendría el beneficio adicional de que podría agregar todas las pruebas de excepción a las pruebas de no lanzar.

¿Es este un diseño válido o me perdí algo?

Editar: Ugs ... parece que el método anterior ExpectException también me deja con 1 bloque no cubierto.

¿Fue útil?

Solución

Lo que estás sugiriendo es válido. Aparte de su problema de cobertura de código, diría que es mejor que usar el atributo ExpectedException ya que muestra explícitamente qué línea de la prueba se espera que arroje la excepción. El uso de ExpectedException significa que cualquier línea de código en la prueba puede generar el tipo de excepción esperado y la prueba aún se aprobará. Si el error se origina en otra llamada que no se esperaba lanzar, puede ocultar el hecho de que la prueba debería fallar porque la línea que debería estar lanzando no lo está.

Lo que sería una modificación útil de lo que ha propuesto sería devolver la excepción detectada:

public static _T ExpectException<_T> (Action action) where _T: Exception
{
    try { action(); }
    catch (_T ex) { return ex; }
    Assert.Fail ("Expected " + typeof(_T));
    return null;
}

Esto permitiría que el código de prueba afirme aún más la excepción si lo desea (es decir, para verificar que se usó un mensaje en particular).

NUnit (aunque no parece que lo estés usando, ya que tienes un atributo TestMethod ) tiene una construcción integrada similar a la que has propuesto:

Assert.Throws<ArgumentNullException>(() => Foo.DoStuff(null))

Otros consejos

@adrianbanks la ExpectException no funciona como se esperaba si el parámetro de acción arroja otra excepción que la excepción esperada:

[TestMethod]
public void my_test()
{
    ExpectException<InvalidOperationException>(delegate()
    {
        throw new ArgumentException("hello");
    });
}

Cuando ejecuto el TestMethod " my_test " Acabo de recibir un mensaje que dice que el método de prueba surgió y la excepción System.ArgumentException: hola. En este caso, debería decir "Expected InvalidOperationException". Propongo una nueva versión para el método ExpectException:

public static void VerifierException<T>(Action action) where T : Exception
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        Assert.IsInstanceOfType(ex, typeof(T));
        return;
    }

    Assert.Fail("Aucune exception n'a été déclenchée alors qu'une exception du type " + typeof(T).FullName + " était attendue");
}

Sé que este es un tema antiguo, pero me encontré con el mismo problema.

Finalmente, me pregunté: ¿por qué necesito saber la cobertura de las pruebas? ¡No lo hago! - Así que descartémoslos, para que la cobertura sea más limpia.

En mi proyecto de prueba, agregué un archivo CodeCoverage.runsettings y este es el contenido:

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
        <Configuration>
          <CodeCoverage>
            <ModulePaths>
              <Exclude>
                <ModulePath>.*tests.dll</ModulePath>
                <ModulePath>.*Tests.dll</ModulePath>
                <!-- Add more ModulePath nodes here. -->
              </Exclude>
            </ModulePaths>
          </CodeCoverage>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

Después de seleccionar este archivo Configuración de prueba , mi cobertura de código es del 100%

De esta manera no es necesario 'hackear' el sistema de cobertura del código de prueba de la unidad, solo para lograr el 100% :-)

Sí, esta es una tarifa bastante estándar, muchas de nuestras pruebas hacen lo mismo. Al mismo tiempo, debe preguntarse si no está asignando un valor demasiado alto a la cobertura del código si esas medias sucursales pesan tanto para que valga la pena el esfuerzo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top