Question

J'ai plusieurs testeurs de ce modèle:

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

Il s’avère que la couverture de code marque la ligne de lancer comme une demi-exécution, alors j’obtiens 1 bloc de code non couvert à chaque fois.

Après avoir réfléchi à ce problème pendant un moment, la meilleure solution que je pouvais trouver était d’ajouter un essai / attrape. Comme il s’agit d’un motif répété, je vais créer une méthode d’aide inspirée de

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

Cela aurait l’avantage de pouvoir ajouter tous les tests d’exception aux tests sans rejet.

Est-ce une conception valide ou ai-je oublié quelque chose?

Éditer: Il semble que la méthode ExpectException ci-dessus me laisse également avec un bloc non couvert.

Était-ce utile?

La solution

Ce que vous suggérez est valide. En plus de votre problème de couverture de code, je dirais qu'il vaut mieux utiliser l'attribut ExpectedException car il indique explicitement quelle ligne du test est censée générer l'exception. L'utilisation de ExpectedException signifie que toutes les lignes de code du test peuvent générer le type d'exception attendu et que le test réussira quand même. Si l'erreur provient d'un autre appel qui ne devait pas être émis, cela peut masquer le fait que le test devrait échouer car la ligne à lancer ne l'est pas.

Une modification utile de ce que vous avez proposé serait de renvoyer l'exception interceptée:

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

Cela permettrait au code de test d'affirmer davantage l'exception s'il le souhaite (par exemple, pour vérifier qu'un message particulier a été utilisé).

NUnit (bien qu'il ne semble pas que vous l'utilisiez car vous avez un attribut TestMethod ) a une construction intégrée similaire à celle que vous avez proposée:

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

Autres conseils

@adrianbanks l'exception ExpectException ne fonctionne pas comme prévu si le paramètre action lève une autre exception que l'exception attendue:

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

Lorsque j'exécute la méthode Test " my_test " Je viens de recevoir un message indiquant que la méthode de test a été générée et System.ArgumentException: hello. Dans ce cas, il devrait être indiqué "Expected InvalidOperationException". Je propose une nouvelle version pour la méthode 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");
}

Je sais que c’est un sujet ancien, mais j’ai rencontré le même problème.

Finalement, je me suis demandé: pourquoi dois-je connaître la couverture des tests? Je ne le fais pas! : éliminons-les afin que la couverture soit plus propre.

Dans mon projet de test, j'ai ajouté un fichier CodeCoverage.runsettings et voici le contenu:

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

Après avoir sélectionné ce fichier Paramètres de test , ma couverture de code est de 100%

.

De cette façon, il n’est pas nécessaire de "pirater" le système de couverture de code de test unitaire, juste pour atteindre 100%: -)

Oui, c’est un tarif assez standard - beaucoup de nos tests font de même. Dans le même temps, vous devez vous demander si vous n'attribuez pas une valeur trop élevée à la couverture de code si ces demi-branches pèsent tellement lourd que cela en vaut la peine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top