Je travaille sur un projet java. Je suis nouveau aux tests unitaires. Quel est le meilleur moyen de tester à l'unité les méthodes privées dans les classes java?
Je travaille sur un projet java. Je suis nouveau aux tests unitaires. Quel est le meilleur moyen de tester à l'unité les méthodes privées dans les classes java?
Réponses:
En général, vous ne testez pas directement les méthodes privées. Comme ils sont privés, considérez-les comme un détail d'implémentation. Personne ne va jamais appeler l'un d'eux et s'attendre à ce que cela fonctionne d'une manière particulière.
Vous devriez plutôt tester votre interface publique. Si les méthodes qui appellent vos méthodes privées fonctionnent comme prévu, vous supposez par extension que vos méthodes privées fonctionnent correctement.
En général, je l'éviterais. Si votre méthode privée est si complexe qu'elle nécessite un test unitaire séparé, cela signifie souvent qu'elle méritait sa propre classe. Cela peut vous encourager à l'écrire de manière réutilisable. Vous devez ensuite tester la nouvelle classe et appeler son interface publique dans votre ancienne classe.
D’autre part, la factorisation des détails d’implémentation dans des classes distinctes aboutit à des classes avec des interfaces complexes, de nombreuses données passant entre l’ancienne et la nouvelle classe, ou à une conception qui peut paraître intéressante du point de vue de la programmation orientée objet, mais qui ne fonctionne pas correctement. faire correspondre les intuitions issues du domaine du problème (par exemple, diviser un modèle de tarification en deux éléments pour éviter de tester des méthodes privées n’est pas très intuitif et peut entraîner des problèmes ultérieurement lors de la maintenance / extension du code). Vous ne voulez pas avoir des "classes jumelles" qui sont toujours modifiées ensemble.
Quand je dois choisir entre encapsulation et testabilité, je préfère la seconde. Il est plus important d’avoir le code correct (c’est-à-dire de produire la sortie correcte) qu’une bonne conception POO qui ne fonctionne pas correctement, car elle n’a pas été testée correctement. En Java, vous pouvez simplement donner à la méthode un accès "par défaut" et placer le test unitaire dans le même package. Les tests unitaires font simplement partie du package que vous développez, et il est correct d'avoir une dépendance entre les tests et le code en cours de test. Cela signifie que lorsque vous modifiez l'implémentation, vous devrez peut-être modifier vos tests, mais ce n'est pas grave - chaque modification de l'implémentation nécessite de tester à nouveau le code, et si les tests doivent être modifiés pour le faire, il vous suffit de le faire. il.
En général, une classe peut proposer plusieurs interfaces. Il existe une interface pour les utilisateurs et une interface pour les responsables. Le second peut en exposer davantage pour garantir que le code est correctement testé. Il n'est pas nécessaire que ce soit un test unitaire sur une méthode privée - il peut s'agir, par exemple, de la journalisation. La journalisation "rompt également l'encapsulation", mais nous le faisons quand même, car c'est très utile.
Le test des méthodes privées dépendrait de leur complexité. certaines méthodes privées en une ligne ne nécessiteraient pas réellement un effort supplémentaire de test (on peut aussi en dire autant des méthodes publiques), mais certaines méthodes privées peuvent être aussi complexes que des méthodes publiques et difficiles à tester via l'interface publique.
Ma technique préférée consiste à rendre le package de méthode privé privé, ce qui permettra d'accéder à un test unitaire dans le même package, mais il sera toujours encapsulé à partir de tout autre code. Cela donnera l’avantage de tester directement la logique de la méthode privée au lieu de devoir s’appuyer sur un test de méthode publique pour couvrir toutes les parties d’une logique (éventuellement) complexe.
Si cela est associé à l'annotation @VisibleForTesting dans la bibliothèque Google Guava, vous marquez clairement cette méthode privée du package comme visible uniquement pour le test et, en tant que telle, elle ne devrait pas être appelée par d'autres classes.
Les opposants à cette technique font valoir que cela romprait l'encapsulation et ouvrirait des méthodes privées pour coder dans le même package. Bien que je convienne que cela rompt l’encapsulation et ouvre le code privé à d’autres classes, j’affirme que le test d’une logique complexe est plus important que l’ encapsulation stricte et que le fait de ne pas utiliser de méthodes de package privé clairement marquées comme visibles uniquement pour le test doit incomber aux développeurs. utiliser et changer la base de code.
Méthode privée avant de tester:
private int add(int a, int b){
return a + b;
}
Méthode privée du paquet prête à être testée:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Remarque: Le fait de placer les tests dans le même package ne revient pas à les placer dans le même dossier physique. La séparation de votre code principal et du code de test dans des structures de dossiers physiques distinctes est une bonne pratique en général, mais cette technique fonctionnera aussi longtemps que les classes sont définies comme dans le même package.
Si vous ne pouvez pas utiliser d'API externe ou si vous ne le souhaitez pas, vous pouvez toujours utiliser l'API JDK standard pure pour accéder aux méthodes privées à l'aide de la réflexion. Voici un exemple
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Consultez le didacticiel Java http://docs.oracle.com/javase/tutorial/reflect/ ou l'API Java http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary. html pour plus d'informations.
Comme le mentionnait @kij dans sa réponse, il est parfois utile de tester une méthode privée avec une solution simple utilisant la réflexion.
Cas de test unitaire signifie tester l'unité de code. Cela ne signifie pas tester l'interface, car si vous testez l'interface, cela ne signifie pas que vous testez l'unité de code. Cela devient une sorte de test de boîte noire. En outre, il est préférable de rechercher les problèmes au niveau de l'unité la plus petite que de déterminer les problèmes au niveau de l'interface et d'essayer ensuite de déboguer le composant qui ne fonctionnait pas. Par conséquent, les tests unitaires doivent être testés indépendamment de leur portée. Voici un moyen de tester les méthodes privées.
Si vous utilisez java, vous pouvez utiliser jmockit qui fournit Deencapsulation.invoke pour appeler n’importe quelle méthode privée de la classe sous test. Il utilise la réflexion pour l'appeler éventuellement, mais fournit une belle enveloppe autour de lui. ( https://code.google.com/p/jmockit/ )
Tout d'abord, comme d'autres auteurs l'ont suggéré: réfléchissez-y à deux fois si vous avez vraiment besoin de tester une méthode privée. Et si oui, ...
Dans .NET, vous pouvez le convertir en méthode "Internal" et créer le package " InternalVisible " dans votre projet de test unitaire.
En Java, vous pouvez écrire des tests dans la classe à tester et vos méthodes de test doivent également pouvoir appeler des méthodes privées. Je n'ai pas beaucoup d'expérience Java, ce n'est donc probablement pas la meilleure pratique.
Merci.
Je fais habituellement ces méthodes protégées. Disons que votre classe est en:
src/main/java/you/Class.java
Vous pouvez créer une classe de test en tant que:
src/test/java/you/TestClass.java
Vous avez maintenant accès aux méthodes protégées et pouvez les tester à l'unité (JUnit ou TestNG n'a pas vraiment d'importance), mais vous conservez ces méthodes des appelants que vous ne vouliez pas.
Notez que cela attend une arborescence source de style maven.
Si vous avez vraiment besoin de tester une méthode privée, avec Java je veux dire, vous pouvez utiliser fest assert
et / ou fest reflect
. Il utilise la réflexion.
Importez la bibliothèque avec maven (les versions données ne sont pas les dernières, je pense) ou importez-la directement dans votre chemin de classe:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
Par exemple, si vous avez une classe nommée 'MyClass' avec une méthode privée nommée 'myPrivateMethod' qui prend un paramètre String et met à jour sa valeur avec 'this is cool testing!', Vous pouvez effectuer le test Junit suivant:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
Cette bibliothèque vous permet également de remplacer toutes les propriétés de haricot (qu'elles soient privées et qu'aucun séparateur ne soit écrit) par un simulacre, et l'utiliser avec Mockito ou tout autre framework simulé est vraiment cool. La seule chose que vous devez savoir pour le moment (je ne sais pas si cela sera meilleur dans les prochaines versions) est le nom du champ / méthode cible que vous souhaitez manipuler, ainsi que sa signature.
Ce que je fais normalement en C #, c’est de rendre mes méthodes protégées et non privées. C'est un modificateur d'accès légèrement moins privé, mais il cache la méthode de toutes les classes qui n'héritent pas de la classe sous test.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Toute classe qui n'hérite pas directement de classUnderTest n'a aucune idée que methodToTest existe même. Dans mon code de test, je peux créer une classe de test spéciale qui étend et donne accès à cette méthode ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Cette classe n'existe que dans mon projet de test. Son seul but est de donner accès à cette méthode unique. Cela me permet d'accéder à des endroits que la plupart des autres classes n'ont pas.
protected
fait partie de l'API publique et présente les mêmes limitations (ne peut jamais être modifié, doit être documenté, ...). En C #, vous pouvez utiliser internal
avec InternalsVisibleTo
.
Vous pouvez facilement tester des méthodes privées si vous placez vos tests unitaires dans une classe interne de la classe que vous testez. En utilisant TestNG, vos tests unitaires doivent être des classes internes statiques publiques annotées avec @Test, comme ceci:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Puisqu'il s'agit d'une classe interne, la méthode private peut être appelée.
Mes tests sont exécutés à partir de maven et il trouve automatiquement ces cas de test. Si vous voulez juste tester une classe, vous pouvez le faire
$ mvn test -Dtest=*UserEditorTest
Source: http://www.ninthavenue.com.au/how-to-unit-test-private-methods