Junit: fractionnement du test d'intégration et des tests unitaires

126

J'ai hérité d'une charge de tests Junit, mais ces tests (à part la plupart ne fonctionnent pas) sont un mélange de tests unitaires réels et de tests d'intégration (nécessitant des systèmes externes, une base de données, etc.).

J'essaie donc de penser à un moyen de les séparer, de sorte que je puisse exécuter le test unitaire rapidement et rapidement et les tests d'intégration après cela.

Les options sont ..

  1. Divisez-les en répertoires séparés.

  2. Passez à Junit4 (à partir de la v3) et annotez les classes pour les séparer.

  3. Utilisez une convention de dénomination de fichier pour dire ce qu'est une classe, c'est-à-dire AdapterATest et AdapterAIntergrationTest.

3 a le problème qu'Eclipse a la possibilité de "Exécuter tous les tests dans le projet / package ou dossier sélectionné". Il serait donc très difficile d'exécuter simplement les tests d'intégration.

2: court le risque que les développeurs commencent à écrire des tests d'intégration dans des classes de tests unitaires et cela devient juste compliqué.

1: Cela semble être la solution la plus soignée, mais mon instinct dit qu'il doit y avoir une meilleure solution là-bas.

C'est donc ma question, comment séparez-vous les tests d'intégration et les tests unitaires appropriés?

jeff porter
la source
Je voudrais simplement vous remercier tous pour votre contribution, je sais que c'est une question subjective, et pas une bonne réponse. Mais vous m'avez aidé à réaliser qu'il n'y a pas d'autres options que celles que j'ai énumérées. Je pense que je vais utiliser la structure du répertoire pour le moment et passer à JUnit4, bien que je n'utilise pas encore d'annotations pour les fractionner.
jeff porter

Réponses:

10

J'utilise actuellement des répertoires séparés en raison de la politique organisationnelle (et de l'héritage de Junit 3), mais je cherche moi-même à passer aux annotations maintenant que je suis sur Junit 4.

Je ne serais pas trop préoccupé par les développeurs qui mettent des tests d'intégration dans vos classes de tests unitaires - ajoutez une règle dans vos normes de codage si nécessaire.

Je suis intéressé de savoir quelles sortes d'autres solutions pourraient exister en dehors des annotations ou de la séparation physique des classes.

Steven Mackenzie
la source
145

Vous pouvez les diviser très facilement en utilisant les catégories JUnit et Maven.

Ceci est montré très, très brièvement ci-dessous en fractionnant les tests unitaires et d'intégration.

Définir une interface de marqueur

La première étape du regroupement d'un test à l'aide de catégories consiste à créer une interface de marqueurs.

Cette interface sera utilisée pour marquer tous les tests que vous souhaitez exécuter comme tests d'intégration.

public interface IntegrationTest {}

Marquez vos classes de test

Ajoutez l'annotation de catégorie en haut de votre classe de test. Il prend le nom de votre nouvelle interface.

import org.junit.experimental.categories.Category;
@Category(IntegrationTest.class)
public class ExampleIntegrationTest{
  @Test
  public void longRunningServiceTest() throws Exception {
  }
}

Configurer les tests unitaires Maven

La beauté de cette solution est que rien ne change vraiment pour le côté test unitaire des choses.

Nous ajoutons simplement une configuration au plugin maven surefire pour qu'il ignore les tests d'intégration.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.11</version>
  <dependencies>
   <dependency>
     <groupId>org.apache.maven.surefire</groupId>
     <artifactId>surefire-junit47</artifactId>
     <version>2.12</version>
   </dependency>
  </dependencies>
  <configuration>
    <includes>
      <include>**/*.class</include>
    </includes>
    <excludedGroups>com.test.annotation.type.IntegrationTest</excludedGroups>
  </configuration>
</plugin>

Lorsque vous effectuez un test de nettoyage mvn, seuls vos tests unitaires non marqués seront exécutés.

Configurer les tests d'intégration Maven

Là encore, la configuration est très simple.

Pour exécuter uniquement les tests d'intégration, utilisez ceci:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.11</version>
  <dependencies>
   <dependency>
     <groupId>org.apache.maven.surefire</groupId>
     <artifactId>surefire-junit47</artifactId>
     <version>2.12</version>
   </dependency>
  </dependencies>
  <configuration>
    <groups>com.test.annotation.type.IntegrationTest</groups>
  </configuration>
</plugin>

Si vous encapsulez ceci dans un profil avec id IT, vous ne pouvez exécuter que les tests rapides à l'aide de mvn clean install. Pour exécuter uniquement les tests d'intégration / lent, utilisez mvn clean install -P IT.

Mais le plus souvent, vous voudrez exécuter les tests rapides par défaut et tous les tests avec -P IT. Si tel est le cas, vous devez utiliser une astuce:

<profiles>
    <profile>
        <id>IT</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <excludedGroups>java.io.Serializable</excludedGroups> <!-- An empty element doesn't overwrite, so I'm using an interface here which no one will ever use -->
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Comme vous pouvez le voir, j'exclus les tests annotés avec java.io.Serializable. Ceci est nécessaire car le profil héritera de la configuration par défaut du plugin Surefire, donc même si vous dites <excludedGroups/>ou <excludedGroups></excludedGroups>, la valeur com.test.annotation.type.IntegrationTestsera utilisée.

Vous ne pouvez pas non plus l'utiliser nonecar il doit s'agir d'une interface sur le chemin de classe (Maven vérifiera cela).

Remarques:

  • La dépendance à surefire-junit47n'est nécessaire que lorsque Maven ne bascule pas automatiquement vers le runner JUnit 4. L'utilisation de l' élément groupsou excludedGroupsdevrait déclencher le commutateur. Regardez ici .
  • La plupart du code ci-dessus a été extrait de la documentation du plugin Maven Failsafe. Consultez la section "Utilisation des catégories JUnit" sur cette page .
  • Au cours de mes tests, j'ai constaté que cela fonctionne même lorsque vous utilisez des @RunWith()annotations pour exécuter des suites ou des tests basés sur Spring.
John Dobie
la source
18
Je pense qu'il y a une erreur dans votre dernier fragment pom.xml. vous avez collé le même extrait que pour la phase "test". il exclut toujours les tests d'intégration et n'est pas non plus lié à une phase maven.
Alex
1
En effet, le dernier fragment de pom est une erreur de copier-coller. Il devrait afficher le plugin maven-failafe-plugin.
Paulo Merson
2
Alors, quel devrait être le deuxième xml alors? : O
Liv
Vous n'avez pas besoin d'utiliser l'astuce (dernière magie avec Serializable) si vous utilisez le profil Maven par défaut
user831217
Cela devrait vraiment être la réponse acceptée car il s'agit en fait d'une solution à la question au lieu d'un débat philosophique sur l'endroit où placer les différents tests.
Bwvolleyball
40

Nous utilisons Maven Surefire Plugin pour exécuter des tests unitaires et Maven Failsafe Plugin pour exécuter des tests d'intégration. Les tests unitaires suivent les **/Test*.java **/*Test.java **/*TestCase.javaconventions de dénomination, les tests d'intégration - **/IT*.java **/*IT.java **/*ITCase.java. C'est donc en fait votre option numéro trois.

Dans quelques projets, nous utilisons TestNG et définissons différents groupes de test pour l'intégration / les tests unitaires, mais cela ne vous convient probablement pas.

Lexicore
la source
1
+1 pour le combo maven + infaillible + sécurité + junit. Je ne savais pas que la sécurité intégrée exécuterait "IT *" automatiquement. Doux.
PapaFreud
13

Je passerais à Junit4 juste pour l'avoir :)

Vous pouvez les séparer en différentes suites de tests. Je ne sais pas comment ils sont organisés dans Junit3 mais il devrait être facile dans Junit4 de créer des suites de tests et de mettre tous les tests unitaires réels dans l'un d'eux, puis d'utiliser une deuxième suite pour les tests d'intégration.

Définissez maintenant une configuration d'exécution pour les deux suites dans eclipse et vous pouvez facilement exécuter une seule suite. Ces suites pourraient également être lancées à partir d'un processus automatisé vous permettant d'exécuter les tests unitaires à chaque fois que la source change et peut-être les tests d'intégration (s'ils sont vraiment volumineux) une seule fois par jour ou une fois par heure.

Janusz
la source
9

L' utilisation de l'annotation de printemps IfProfileValue permet d'y parvenir sans plug-in maven ou configuration requise.

Annoter les classes ou méthodes de test d'intégration à l'aide de IfProfileValue

import org.springframework.test.annotation.IfProfileValue;

@IfProfileValue(name="test-groups", value="integration")
public class ExampleIntegrationTest{
    @Test
    public void longRunningServiceTest() throws Exception {
    }
} 

Pour exécuter à l'aide de tests unitaires uniquement:

mvn clean test

Pour exécuter à l'aide du test d'intégration et des tests unitaires:

mvn clean test -Dtest-groups=integration

De plus, "Exécuter tous les tests" dans un IDE n'exécuterait que des tests unitaires. Ajoutez des -Dtest-groups=integrationarguments à la VM pour exécuter à la fois l'intégration et les tests unitaires.

Mit Mehta
la source
Cette approche est simple et agréable, mais le problème que je trouve est que, par défaut, j'aimerais exécuter tous les tests (y compris les tests d'intégration). Ce n'est pas possible avec cette approche, n'est-ce pas?
csalazar
6

Il n'y a pas une seule bonne réponse. Comme vous l'avez expliqué, il existe plusieurs façons de le faire qui fonctionneront. J'ai fait à la fois le schéma de nommage des fichiers et la division des choses dans différents répertoires.

Il semble que diviser quelque chose en différents répertoires pourrait mieux fonctionner pour vous, et cela me semble un peu plus clair, alors je me pencherais vers cela.

Je ne pense pas que j'essaierais les annotations parce que cela me semble plus précis. Voulez-vous vraiment que ces deux types de tests soient mélangés dans le même fichier? Je ne le ferais pas.

ndp
la source