2010-09-14 14 views
1

Je travaille sur un projet au moment où je dois interagir avec du code qui avale des exceptions. En particulier, j'écris des tests unitaires NUnit. Il y a des endroits où je veux incorporer des assertions dans le code qui est passé en tant que délégué, dans le cadre d'un comportement particulier. Le problème que j'ai est que l'exception AssertionException est avalée par le code appelant le délégué, ce qui signifie que le test passe, même si le test Assert a échoué.Empêcher la capture de NUnit AssertionException?

Est-il possible d'informer NUnit qu'un test doit échouer et qu'il ne peut pas être contourné en interceptant AssertionException? Je ne peux pas modifier le code qui avale les exceptions, car je n'ai pas la pleine propriété et c'est déjà en semi-production. J'espère qu'il y a une manière propre d'accomplir ceci.

Le meilleur que je suis venu avec est quelque chose comme ceci:

private static string _assertionFailure; 
    public static void AssertWrapper(Action action) 
    { 
     try 
     { 
      action(); 
     } 
     catch (AssertionException ex) 
     { 
      _assertionFailure = ex.Message; 
      throw; 
     } 
    } 

    [Test] 
    [ExpectedException(typeof(AssertionException))] 
    public void TestDefeatSwallowing() 
    { 
     Action failure =() => AssertWrapper(() => Assert.Fail("This is a failure")); 

     EvilSwallowingMethod(failure); 

     if (_assertionFailure != null) 
      Assert.Fail(_assertionFailure); 
    } 

    private void EvilSwallowingMethod(Action action) 
    { 
     try 
     { 
      action(); 
     } 
     catch 
     { 
     } 
    } 

Il fonctionne, mais il est assez laid. Je dois boucler chaque appel Assert et je dois vérifier à la fin de chaque test si une assertion a été avalée.

+1

Pas exactement lié - avez-vous insisté sur le fait que ce code avale des exceptions dans la chaîne? Le code de semi-production devrait être beaucoup plus facile à changer (selon les circonstances, bien sûr). Relié - cela ressemble à une méthode assez intelligente de le faire. Vous devez contourner un modèle extrêmement laid - c'est très rarement facile à faire proprement. – arootbeer

+1

@arootbeer, l'équipe avec laquelle je travaille est consciente que c'est un problème, mais c'est un problème très systémique et lié à la perception de l'utilisateur final qu'un «crash» est la pire chose qu'un système peut faire. Il a été suggéré à la blague que le code pourrait être modifié pour n'avaler que des exceptions entre les heures de 19 heures et 7 heures du matin. –

+0

Wow - Je n'irai pas plus loin avec ça :) Est-ce que toutes les méthodes que vous testez acceptent une action comme paramètre? – arootbeer

Répondre

2

Alors, vous faites quelque chose comme ça? (Ce qui est en utilisant la syntaxe Moq)

var dependency1 = new Mock<IDependency1>(); 
dependency1.Setup(d => d.CalledMethod([Args]) 
    .Callback(TestOutArgsAndPossiblyThrow); 

var objectUnderTest = new TestedObject(dependency1.Object); 
objectUnderTest.MethodThatCallsIDependency1dotCalledMethod(); 

Et vous avez TestOutArgsAndPossiblyThrow encapsulé dans votre classe AssertWrapper?

À moins que ce soit loin, je dirais que vous le faites à peu près juste. Vous avez l'exécution de ré-entrer votre test à un moment où vous pouvez enregistrer l'état de l'appel à la dépendance. Que ce soit fait en attrapant des exceptions et en les analysant ou en inspectant directement les valeurs des paramètres de la méthode, vous devez faire le travail. Et si vous avalez des exceptions à l'intérieur de la boîte noire, vous devrez les surveiller avant de revenir dans la boîte noire. Je dis toujours que vous seriez bien mieux lotis avec la journalisation et la notification appropriées (vous ne devez pas nécessairement informer les utilisateurs finaux). Pour @ le point de TrueWill - que faire vous faites quand il ya un IOException ou la base de données n'est pas disponible?

interlangue

Votre scénario structuré comme celui-ci?

TEST -> TESTED CODE -> SWALLOWING CODE -> THROWING MOCK 
+1

Ce qui précède est fondamentalement ce que je fais, oui. Le code de production possède une journalisation pour (la plupart) des emplacements de capture, mais il empêche toujours la propagation d'exceptions. La déglutition se produit dans un code dont je dépend, mais qui est essentiel à la mise en œuvre de la logique du code que j'ai écrit et que je suis en train de tester, donc il ne faut pas se moquer de lui. Je suppose que je pourrais créer une copie de la classe qui n'accepte pas les exceptions et utiliser cette copie uniquement pour mon code, mais cela crée d'autres problèmes politiques et de maintenance. –