2008-08-11 26 views
1

Quelle est la meilleure façon de tester une fonction qui provoque une erreur? Ou tester une fonction qui est assez à l'abri de l'échec?Test d'une fonction qui déclenche une erreur

Par exemple; J'ai une classe de port d'achèvement d'E/S qui jette dans le constructeur s'il ne peut pas initialiser le port correctement. Cela utilise la fonction Win32 de CreateIoCompletionPort dans la liste initialiser. Si le handle n'est pas défini correctement (valeur non nulle), le constructeur lève une exception. Je n'ai jamais vu cette fonction échouer.

Je suis assez certain que ce (et d'autres fonctions comme dans mon code) s'ils ne se comporteront correctement, le code est 50 lignes longues, y compris l'espace blanc, donc mes questions sont

a) est-il b) et s'il vaut la peine de tester, comment?
c) Les classes d'emballage simples devraient-elles être soumises à des tests unitaires? Pour b) J'ai pensé à redéfinir CreateIoCompletionPort et à passer les valeurs à travers. Dans le test unitaire le remplacer et l'amener à retourner 0 quand une certaine valeur est transmise. Cependant, puisque cela est utilisé dans le constructeur, alors cela doit être statique. Cela semble-t-il valide ou non?

Répondre

2

Il est vraiment intéressant de tester les conditions d'échec, à la fois que votre classe lève correctement une exception quand vous le souhaitez et que les exceptions sont gérées correctement dans la classe.

Cela peut être facilement fait si vous agissez sur un objet passé dans le constructeur ... juste passer dans un simulacre. Si ce n'est pas le cas, j'ai tendance à préférer que la fonctionnalité soit déplacée vers une méthode protégée et remplacer la méthode protégée pour évoquer mon cas de défaillance. Je vais utiliser Java comme un exemple, mais il devrait être assez facile de porter les idées à un C# cas:

public class MyClass { 
    public MyClass() throws MyClassException { 
     // Whatever, including a call to invokeCreateIoCompletionPort 
    } 

    protected int invokeCreateIoCompletionPort(String str, int i) { 
     return StaticClass.createIoCompletionPort(str, i); 
    } 
} 

public class MyTest { 
    public void myTest() { 
     try { 
      new MyClass(); 
      fail("MyClassException was not thrown!"); 
     } catch (MyClassException e) { 
     } 
    } 

    private static class MyClassWrapper extends MyClass { 
     @Override 
     protected int invokeCreateIoCompletionPort(String str, int i) { 
      throw new ExpectedException(); 
     } 
    } 
} 

Comme vous pouvez le voir, il est assez facile de vérifier si une exception est levée par le constructeur ou méthode que vous testez, et il est également très facile d'injecter une exception d'une classe externe qui peut déclencher une exception. Désolé, je n'utilise pas votre méthode actuelle, j'ai juste utilisé le nom pour illustrer comment cela sonnait comme si vous l'utilisiez, et comment je testerais les cas que vous vouliez tester. Fondamentalement, tous les détails de l'API que vous exposer peuvent généralement être testés, et si vous voulez savoir que les cas exceptionnels fonctionnent comme ils le devraient, vous aurez probablement envie de le tester.

1

Vous devriez envisager d'écrire votre code de telle manière que vous puissiez simuler votre port de complétion d'E/S. Créez une interface/classe abstraite qui expose les méthodes dont vous avez besoin sur l'objet d'E/S, et écrivez et testez l'implémentation qui fait les choses comme elle est supposée le faire (et une option pour simuler l'échec peut-être). Je sais que c'est une pratique courante de se moquer des ressources externes lors des tests unitaires, afin de minimiser les dépendances.

3

Si vous faites cela dans .NET, il est un attribut ExpectedException que vous pouvez ajouter à votre test:

[Test, ExpectedException(typeof(SpecificException), "Exception's specific message")] 
public void TestWhichHasException() 
{ 
    CallMethodThatThrowsSpecificException(); 
} 

test passera si l'exception de ce type et avec le message spécifié est jeté. L'attribut a d'autres surcharges, y compris des InnerExceptions, etc.

0

Sonne comme C++ pour moi. Vous avez besoin d'une couture pour simuler les fonctions Win32. Par exemple.Dans votre classe, vous créez une méthode protégée CreateIoCompletionPort() qui appelle ::CreateIoCompletionPort() et pour votre test, vous créez une classe dérivée de votre classe de port d'achèvement d'E/S et remplace CreateIoCompletionPort() pour ne rien faire d'autre que renvoyer NULL. Votre classe de production se comporte toujours comme si elle avait été conçue, mais vous pouvez maintenant simuler une défaillance dans la fonction CreateIoCompletionPort().

Cette technique est tirée du livre de Michael Feathers «Working efficacement with legacy code».