Domanda

Ho diversi unittest di questo modello:

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

Si scopre che la copertura del codice segna la linea di lancio a metà corsa, quindi ottengo 1 blocco di codice scoperto ogni volta.

Dopo aver pensato a questo problema per un po ', la soluzione migliore che ho potuto trovare è stata l'aggiunta di un tentativo / cattura. Dato che questo è uno schema ripetuto, ho intenzione di creare un metodo di supporto lungo le linee di

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

Ciò avrebbe il vantaggio secondario di poter aggiungere tutti i test delle eccezioni ai test non gettanti.

È un progetto valido o mi sono perso qualcosa?

Modifica: Ugs ... sembra che il precedente metodo ExpectException mi lasci anche con 1 blocco scoperto.

È stato utile?

Soluzione

Ciò che stai suggerendo è valido. A parte il problema di copertura del codice, direi che è meglio che usare l'attributo ExpectedException poiché mostra esplicitamente quale linea del test dovrebbe generare l'eccezione. L'uso di ExpectedException significa che qualsiasi riga di codice nel test può generare il tipo di eccezione previsto e il test continuerà comunque. Se l'errore proviene da un'altra chiamata che non si prevedeva di lanciare, può nascondere il fatto che il test dovrebbe fallire perché la linea che dovrebbe essere lanciata non lo è.

Quale sarebbe una modifica utile a ciò che hai proposto sarebbe di restituire l'eccezione rilevata:

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

Ciò consentirebbe al codice di prova di far valere ulteriormente l'eccezione se lo desiderasse (ad esempio per verificare che fosse utilizzato un determinato messaggio).

NUnit (anche se non sembra che tu lo stia usando in quanto hai un attributo TestMethod ) ha un costrutto incorporato simile a quello che hai proposto:

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

Altri suggerimenti

@adrianbanks ExpectException non funziona come previsto se il parametro action genera un'altra eccezione rispetto all'eccezione prevista:

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

Quando eseguo il TestMethod " my_test " ho appena ricevuto un messaggio che dice che il metodo di prova è stato sollevato e System.ArgumentException: ciao. In questo caso dovrebbe essere indicato "Expected InvalidOperationException". Propongo una nuova versione per il metodo 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");
}

So che questo è un vecchio argomento, ma ho riscontrato lo stesso problema.

Alla fine mi sono interrogato: perché devo conoscere la copertura dei test? Non lo so! - Quindi escludiamoli, quindi la copertura è più pulita.

Nel mio progetto di test ho aggiunto un file CodeCoverage.runsettings e questo è il contenuto:

<?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>

Dopo aver selezionato questo file Impostazioni test la mia copertura del codice è del 100%

In questo modo non è necessario "hackerare" il sistema di copertura del codice di unit test, solo per raggiungere il 100% :-)

Sì, questa è una tariffa piuttosto standard - molti dei nostri test fanno lo stesso. Allo stesso tempo, devi chiederti se non stai attribuendo un valore troppo alto alla copertura del codice se quei semi-rami pesano così tanto da valerne la pena.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top