2009-05-30 7 views
8

Il est courant d'avoir des classes avec des méthodes avec des paramètres de chaîne qui doivent être validées agains nul ou vide, comme cet exemple:Comment tester les paramètres de chaîne vides et/ou vides dans une méthode?

public class MyClass { 

    public void MyMethod(string param){ 

     if(string.IsNullOrEmpty(param)){ 
      throw new ArgumentNullException(...); 
     } 
     //... 
    } 
} 

Il est clair que le comportement de la méthode est la même pour les deux (invalide) valeurs. C'est une situation très courante, et quand il s'agit de tester ces méthodes, je doute toujours de la façon de le faire. Je crée toujours deux tests distincts pour ces cas:

[TestClass] 
public class Tests { 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_null(){ 
     //... 
     myclass.MyMethod(null); 
     //... 
    } 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_empty(){ 
     //... 
     myclass.MyMethod(""); 
     //... 
    } 

}

Mais je vois trop de redondance. Ces tests sont exactement les mêmes, la seule différence étant le paramètre passé à la méthode. Cela me dérange beaucoup, puisque je dois créer deux tests pour chaque paramètre de chaîne. Une méthode avec 3 paramètres aurait 6 tests seulement pour tester les paramètres.

Je pense que c'est la bonne façon de tester ces paramètres, mais si je sais que 99% des paramètres de chaîne seront validés de la même manière, ne serait-il pas mieux de les tester pour null (et vide) que le comportement dans l'autre cas sera le même?

Je voudrais savoir ce que vous en pensez. Je sais que ce que je demande est plus une opinion technique qu'une question technique, mais je pense que la communauté de test peut avoir quelque chose d'intéressant à dire sur cette situation.

Merci!

Répondre

11

Personnellement, je considérerais en utilisant un seul test pour tous les paramètres. Cela ne suit pas le dogme normal des tests unitaires, mais il augmente la lisibilité des tests (en minimisant la quantité de code de test qui est dédiée à un cas assez répétitif) et n'a pas beaucoup d'inconvénients. Oui, si le test échoue, vous ne savez pas si tous les contrôles après le premier échec échoueront également - mais est-ce que est vraiment un problème en pratique?

Le point important est de s'assurer que vous avez un raccourci pour tester le cas. Par exemple, vous pourriez écrire quelque chose comme ça (si votre cadre de test unitaire ne l'a pas déjà):

public static void ExpectException<T>(Action action) where T : Exception 
{ 
    try 
    { 
     action(); 
     Assert.Fail("Expected exception " + typeof(T).Name); 
    } 
    catch (T exception) 
    { 
     // Expected 
    } 
} 

Ensuite, vous pouvez écrire:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    ExpectException<ArgumentNullException>(() => myClass.MyMethod(null)); 
    ExpectException<ArgumentException>(() => myClass.MyMethod("")); 
} 

beaucoup plus concis que de faire chacun avec un bloc try/catch individuel, ou même en utilisant un attribut ExpectedException et plusieurs tests.

Vous pouvez avoir besoin de surcharges pour vérifier que, dans chaque cas, aucun objet manipulé n'a été touché (pour vérifier que les effets indésirables sont évités) ou peut être surchargé pour les exceptions courantes telles que ArgumentNullException.

Pour les méthodes à un seul paramètre, vous pouvez même écrire une méthode pour encapsuler exactement ce dont vous avez besoin:

public void ExpectExceptionForNullAndEmptyStrings(Action<string> action) 
{ 
    ExpectException<ArgumentNullException>(() => action(null)); 
    ExpectException<ArgumentException>(() => action("")); 
} 

puis appelez avec:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    // This *might* work without the 
    ExpectExceptionForNullAndEmptyStrings(myClass.MyMethod); 
} 

... et peut-être une autre pour les méthodes avec un seul paramètre mais un type de retour non-nul.

C'est peut-être aller un peu loin que :)

+0

Merci pour votre réponse rapide! J'ai déjà créé ma propre version de ExpectException (vous pouvez la consulter ici http://gerardocontijoch.wordpress.com/2008/12/02/uso-de-expectedexceptionattribute-para-testear-excepciones/), et l'utiliser dans ces cas. Je ne peux pas vivre sans: P J'aime la solution que vous proposez, j'y ai pensé, mais comme vous l'avez dit, "cela ne suit pas le dogme normal des tests unitaires" et je voulais seulement le suivre, vous savez , s'habituer aux bonnes pratiques. Je vais probablement aller chercher cette solution après tout. Voyons ce que les autres pensent ... –

+1

Il est, je ne crois pas que suivre le dogme et jeter le pragmatisme par la fenêtre * est * une bonne pratique :) Il vaut la peine de * savoir * le dogme, mais en ignorant consciemment quand vous J'ai pesé le pour et le contre d'une situation particulière et je l'ai trouvé ne pas fonctionner. –

+0

Je suis totalement d'accord avec vous et c'est la raison de ma question. J'ai trouvé les inconvénients de suivre une certaine "bonne" pratique, mais pas beaucoup de pros (sorte de contradiction, bonne pratique avec plus de contre que de pros), alors j'ai posté une question pour voir s'il y avait quelque chose qui me manquait. –

-2

Si vous utilisez Java et JUnit vous pouvez utiliser cette syntaxe

@Test(expected = IllegalArgumentException.class) 
public void ArgumentTest() { 
    myClass.MyMethod(""); 
    myClass.MyMethod(null); 
} 
+4

Cette méthode passera si * au moins un * de ces appels de méthode renvoie la bonne exception - plutôt que si * both * le fait. C'est une erreur facile à commettre, malheureusement - ce genre de test n'est utile que (IMO) quand il s'agit d'une seule déclaration. Sinon, vous ne testez pas exactement l'emplacement de l'exception. –

+0

Oui, vous avez raison ... devrait être deux tests ... – Janusz

Questions connexes