Comment vérifier «aucune exception ne s'est produite» dans mon test unitaire MSTest?

88

J'écris un test unitaire pour cette méthode qui renvoie "void". Je voudrais avoir un cas où le test réussit lorsqu'il n'y a pas d'exception levée. Comment écrire cela en C #?

Assert.IsTrue(????)

(Je suppose que c'est ainsi que je devrais vérifier, mais qu'est-ce qui se passe dans "???")

J'espère que ma question est suffisamment claire.

CurieuxGeorge
la source
Utilisez-vous MSTest ou NUnit?
Matt Grande
2
Dans MSTest, les exceptions non interceptées entraîneront automatiquement l'échec des tests. Essayez-vous de tenir compte des exceptions interceptées?
Phil
Vous pouvez rechercher «try-catch pour C #» et cela vous indiquera comment gérer les exceptions levées ou non levées.
Foggzie
1
Si NUnit, regardez dans Assert.That (lambda) .Throws.Nothing (bien que je pense que cela a changé récemment)
Matt Grande

Réponses:

138

Votre test unitaire échouera de toute façon si une exception est levée - vous n'avez pas besoin de mettre une assertion spéciale.

C'est l'un des rares scénarios où vous verrez des tests unitaires sans aucune assertion - le test échouera implicitement si une exception est déclenchée.

Cependant, si vous voulez vraiment écrire une assertion pour ceci - peut-être pour pouvoir attraper l'exception et signaler "n'attend pas d'exception mais a obtenu ceci ...", vous pouvez le faire:

[Test]
public void TestNoExceptionIsThrownByMethodUnderTest()
{
    var myObject = new MyObject();

    try
    {
        myObject.MethodUnderTest();
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected no exception, but got: " + ex.Message);
    }
}

(ce qui précède est un exemple pour NUnit, mais il en va de même pour MSTest)

Rob Levine
la source
Évidemment, vous ne devriez pas aller chercher de telles exceptions pour que cela soit vrai.
Servy
7
Un test échouera uniquement si une exception non interceptée est lancée. En fonction du code dans les gestionnaires d'exceptions, les tests unitaires peuvent réussir.
ediblecode
1
C'est utile pour Mme Unittest, il n'y a donc pas de méthode Assert.DoesNotThrow (() dans Unittest.
Başar Kaya
25

Dans NUnit, vous pouvez utiliser:

Assert.DoesNotThrow(<expression>); 

pour affirmer que votre code ne lève pas d'exception. Bien que le test échoue si une exception est levée même s'il n'y avait pas d'assert autour d'elle, la valeur de cette approche est que vous pouvez alors faire la distinction entre les attentes non satisfaites et les bogues dans vos tests, et vous avez la possibilité d'ajouter un message personnalisé qui sera affiché dans votre sortie de test. Une sortie de test bien formulée peut vous aider à localiser les erreurs dans votre code qui ont provoqué l'échec d'un test.

Je pense qu'il est valable d'ajouter des tests pour s'assurer que votre code ne lève pas d'exceptions; par exemple, imaginez que vous validez une entrée et que vous devez convertir une chaîne entrante en un long. Il peut arriver que la chaîne soit nulle, ce qui est acceptable, vous voulez donc vous assurer que la conversion de chaîne ne lève pas d'exception. Il y aura donc du code pour gérer cette occasion, et si vous n'avez pas écrit de test pour cela, vous manquerez de couverture autour d'un élément de logique important.

Clarkeye
la source
1
Le DoesNotThrow explicite est sympa. Si vous avez l'habitude de voir Assert. * Dans un test, vous pourriez penser que l'autre gars était paresseux et a oublié.
Matt Beckman
Y a-t-il un équivalent pour cela dans vstest ou mstest?
Dan Csharpster
1
@DanCsharpster, je ne pense pas qu'il y en ait, du moins dans MSTest - quand j'ai eu besoin de cette fonctionnalité dans MSTest dans le passé, j'ai fait quelque chose comme ceci: public class TestBase { //believe me, I don't like this anymore than you do. protected void AssertDoesNotThrow(Action action, string message) { try { action(); } catch (Exception) { Assert.Fail(message); } } }
Clarkeye
@Clarkeye, c'est une idée intéressante. Merci! Espérons qu'ils apprendront à mieux copier NUnit, dans les versions futures. J'ai aussi pensé à écrire un adaptateur entre vstest et NUnit.
Dan Csharpster
@DanCsharpster, une chose que vous voudrez peut-être examiner est les assertions fluides, qui ont un bon support pour ShouldThrow et ShouldNotThrow: github.com/dennisdoomen/fluentassertions/wiki#exceptions . La documentation dit qu'il est compatible avec MSTest (même si je ne l'ai utilisé qu'avec XUnit et NUnit). Peut-être ne pas faire tout ce que vous voulez, mais vous pouvez quand même le mélanger avec les assertions MSTest.
Clarkeye
11

Ne testez pas que quelque chose n'arrive pas . C'est comme s'assurer que le code ne casse pas . C'est en quelque sorte implicite, nous nous efforçons tous de trouver du code sans rupture et sans bogue. Tu veux faire des tests pour ça? Pourquoi une seule méthode? Ne voulez-vous pas que toutes vos méthodes soient testées sans faire exception ? En suivant cette route, vous vous retrouverez avec un test supplémentaire, factice et sans affirmation pour chaque méthode de votre base de code. Cela n'apporte aucune valeur.

Bien sûr, si votre exigence est de vérifier la méthode fait des exceptions de capture , vous faites test (ou renverser un peu, test qui ne jette pas ce qu'il est censé attraper).

Cependant, l'approche / les pratiques générales restent intactes - vous n'écrivez pas de tests pour certaines exigences artificielles / vagues qui sont hors de portée du code testé (et tester que "ça marche" ou "ne lance pas" est généralement un exemple de tel - en particulier dans un scénario où les responsabilités de la méthode sont bien connues).

Pour faire simple, concentrez-vous sur ce que votre code doit faire et testez-le.

km
la source
10
-1 Je peux penser à des fonctionnalités positives qui nécessitent et à l'exception de ne pas être levée. Pour une méthode dont le travail consiste à gérer les exceptions, à les consigner et à agir - sans lever davantage l'exception. Vous faites un bon point général - mais parlez ensuite dans l' absolu comme si c'était toujours vrai.
Rob Levine
3
@RobLevine: Je comprends votre exemple et je réalise que vous écrivez des tests dans de tels cas. Pourtant, comme vous l'avez remarqué, mon propos portait en effet sur une pratique plus générale - pour ainsi dire, tester ce que votre code est censé faire par rapport à tester ce que votre code ne fait pas. J'ai reformulé un peu mon message pour que mon propos soit plus clair et plus proche de ce que j'avais en tête. Vous donne également la possibilité de reconsidérer votre vote. Merci pour la clarification et désolé pour la réponse retardée.
km
4
downvote supprimé - Je ne serai pas si heureux lors du vote négatif la prochaine fois!
Rob Levine
4
Dans notre projet, nous avons la classe htmlvalidator, qui lève des exceptions si html n'est pas valide. Par exemple, lorsque l'utilisateur saisit (à l'aide de la console) javascript dans une combinaison riche. Donc, dans mon code de cas, ce que mon code fait est de ne pas lancer d'exception (approche de liste blanche) et je dois tester cela.
Machet
1
Pas d'accord avec cette réponse. Tester l'absence de quelque chose parfois dans un certain scénario peut être un test valide.
bytedev
7

Cette classe d'aide a gratté ma démangeaison avec MSTest. Peut-être que cela peut aussi rayer le vôtre.

[TestMethod]
public void ScheduleItsIneligibilityJob_HasValid_CronSchedule()
{
    // Arrange
    var factory = new StdSchedulerFactory();
    IScheduler scheduler = factory.GetScheduler();

    // Assert
    AssertEx.NoExceptionThrown<FormatException>(() =>
        // Act
        _service.ScheduleJob(scheduler)
    );
}

public sealed class AssertEx
{
    public static void NoExceptionThrown<T>(Action a) where T:Exception
    {
        try
        {
            a();
        }
        catch (T)
        {
            Assert.Fail("Expected no {0} to be thrown", typeof(T).Name);
        }
    }
}
JJS
la source
@Remco Beurskens - l'ajout d'un catch général {} à la fin de NoExceptionThrown <T> supprimera d'autres erreurs, ce qui n'est pas une conséquence voulue de la méthode. Il ne s'agit pas d'une méthode à usage général pour supprimer toutes les exceptions. Il est destiné à échouer uniquement lorsqu'une exception du type connu est levée.
JJS
1
C'est très ancien maintenant, mais il Asserta un accesseur de propriété singleton, Thatque l'on peut utiliser comme crochet pour les méthodes d'extension. Il serait peut-être plus soigné, et plus découvrable, d'avoir Assert.That.DoesNotThrow()plutôt que AssertEx.DoesNotThrow(). Ceci est juste une opinion.
Richard Hauer
3

J'aime voir un Assert.Whateverà la fin de chaque test, juste pour la cohérence ... sans un, puis-je vraiment être sûr qu'il n'y en a pas?

Pour moi, c'est aussi simple que de mettre Assert.IsTrue(true);

Je sais que je n'ai pas accidentellement mis ce code là-dedans, et donc je devrais être suffisamment confiant en un rapide survol que c'était comme prévu.

    [TestMethod]
    public void ProjectRejectsGappedVersioningByDefault() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        Assert.Throws<ScriptProject.InvalidProjectFormatException>(() => {
            var sut = new ScriptProject(files);
        });

    }

    [TestMethod]
    public void ProjectAcceptsGappedVersionsExplicitly() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        var sut = new ScriptProject(files, true);

        Assert.IsTrue(true);   // Assert.Pass() would be nicer... build it in if you like

    }
jleach
la source
Ce n'est pas la même chose. Si votre code est lancé, aucune assertion ne sera atteinte et votre test échouera. Vous souhaitez vous connecter au framework de test en affirmant une condition.
DvS
1

Mon ami Tim m'a parlé de ExpectedException . J'aime vraiment ça car c'est plus succinct, moins de code et très explicite que vous testez une exception.

[TestMethod()]
[ExpectedException(typeof(System.Exception))]
public void DivideTest()
{
    int numerator = 4;
    int denominator = 0;
    int actual = numerator / denominator;
}

Vous pouvez en savoir plus ici: Utilisation d'attributs ExpectedException .

Jess
la source
1
le PO demande une non exception.
Daniel A. White
Je vais laisser cette réponse ici. J'ai trouvé cette question en cherchant sur google pour savoir comment tester les exceptions et cette réponse, je pense, doit être ici. Le PO a répondu à sa question il y a 7 ans. Même le lien vers l'autre réponse, je pense, est utile.
Jess
Bon vieux Tim. 🤔
ruffin le