98 votes

Comment tester la classe principale d'une application Spring-boot ?

J'ai un spring-boot application où mon @SpringBootApplication La classe de démarrage ressemble à une classe standard. J'ai donc créé de nombreux tests pour toutes mes fonctionnalités et envoyé le résumé à sonarqube pour voir ma couverture.

Pour ma classe de démarrage, Sonarqube me dit que je n'ai qu'une couverture de 60%. La couverture moyenne n'est donc pas aussi bonne que prévu.

enter image description here

Ma classe de test est juste la classe par défaut.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ElectronicGiftcardServiceApplication.class)
public class ElectronicGiftcardServiceApplicationTests {

    @Test
    public void contextLoads() {
    }
}

Alors comment puis-je tester ma classe principale dans la classe de démarrage de mon application ?

0 votes

Vous pouvez juste vous moquer SpringApplication à l'aide de mockito et vérifiez qu'elle a été appelée avec les bons arguments lorsque vous exécutez le programme principal. method

0 votes

Voir aussi la solution jacoco gradle : stackoverflow.com/a/43196918/907576

127voto

davidh Points 107

Toutes ces réponses semblent exagérées.
On n'ajoute pas des tests pour faire plaisir à un outil de mesure.
Chargement d'un contexte Spring de l'application prend du temps . Ne l'ajoutez pas dans chaque build de développeur juste pour gagner environ 0,1% de couverture dans votre application.
Ici on ne couvre pas qu'une seule déclaration à partir de 1 méthode publique. Cela ne représente rien en termes de couverture dans une application où des milliers de déclarations sont généralement rédigées .

Première solution : créez votre classe d'application Spring Boot sans déclarer de bean à l'intérieur. Si vous en avez, déplacez-les dans une classe de configuration (pour qu'ils soient toujours couverts par les tests unitaires). Et ensuite, ignorez votre classe d'application Spring Boot dans la classe de configuration. configuration de la couverture des tests.

Deuxième solution de contournement : si vous avez vraiment besoin de couvrir la main() (pour des raisons organisationnelles par exemple), créez un test pour celui-ci, mais un test d'intégration (exécuté par un outil d'intégration continue et non dans chaque build de développeur) et documentez clairement l'objectif de la classe de test :

import org.junit.Test;

// Test class added ONLY to cover main() invocation not covered by application tests.
public class MyApplicationIT {
   @Test
   public void main() {
      MyApplication.main(new String[] {});
   }
}

13 votes

Oui, vous avez raison. Je voulais juste rendre l'outil heureux. Merci pour vos solutions de contournement utiles.

2 votes

Vous êtes le bienvenu :) Et merci pour cette question intéressante :)

2 votes

@davidxxx J'ai utilisé la même approche et cela fonctionne bien aussi. Mais SonarQube dit que les méthodes annotées avec Test doivent avoir au moins une méthode assert. Que peut-on utiliser ici ? Veuillez suggérer.

23voto

fg78nc Points 2371

Vous pouvez faire quelque chose comme ceci

@Test
public void applicationContextLoaded() {
}

@Test
public void applicationContextTest() {
    mainApp.main(new String[] {});
}

13voto

marcpa00 Points 41

J'avais le même objectif (avoir un test qui exécute la méthode main()) et j'ai remarqué que le simple ajout d'une méthode de test comme @fg78nc l'a dit va en fait "démarrer" l'application deux fois : une fois par le framework de test de spring boot, une fois via l'invocation explicite de mainApp.main(new String[] {}) ce que je ne trouve pas élégant.

J'ai fini par écrire deux classes de test : une avec @SpringBootTest et la méthode de test vide applicationContextLoaded() un autre sans @SpringBootTest (seulement RunWith(SpringRunner.class) ) qui appelle la méthode principale.

SpringBootApplicationTest

package example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.boot.test.context.SpringBootTest;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootApplicationTest {

  @Test
  public void contextLoads() {
  }
}

ApplicationStartTest

package example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
public class ApplicationStartTest {
  @Test
  public void applicationStarts() {
    ExampleApplication.main(new String[] {});
  }
}

Globalement, l'application est toujours lancée deux fois, mais parce qu'il y a maintenant deux classes de test. Bien sûr, avec seulement ces deux méthodes de test, cela semble exagéré, mais habituellement, d'autres tests seront ajoutés à la classe SpringBootApplicationTest en profitant de @SpringBootTest installation.

0 votes

Cette solution ne me donne aucune couverture dans Jacoco.

3voto

awgtek Points 42

Vous pouvez simuler SpringApplication puisqu'il s'agit d'une dépendance de la méthode testée. Voir comment aquí . I.e.

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;

import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;

@RunWith(PowerMockRunner.class)
public class ElectronicGiftcardServiceApplicationTest {

    @Test
    @PrepareForTest(SpringApplication.class)
    public void main() {
        mockStatic(SpringApplication.class);
        ElectronicGiftcardServiceApplication.main(new String[]{"Hello", "World"});
        verifyStatic(SpringApplication.class);
        SpringApplication.run(ElectronicGiftcardServiceApplication.class, new String[]{"Hello", "World"});
    }

}

0 votes

C'est tout simplement verifyStatic() sans passer d'arguments

2voto

Mariano LEANCE Points 433
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>your.awesome.package.Application</mainClass> 
    </configuration>
</plugin>

Si vous visez une couverture à 100 %, une chose que vous pouvez faire est de ne pas avoir de méthode main du tout. Vous avez toujours besoin d'une classe annotée avec @SpringBootApplication mais il peut être vide.

Attention toutefois, il a ses inconvénients et d'autres outils qui s'appuient sur la technologie de l'énergie solaire. main peut se briser.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X