web-dev-qa-db-fra.com

Comment tester la classe principale de l'application Spring-Boot

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

Sonarqube me dit que je n'ai qu'une couverture de 60%. Donc, la couverture moyenne n'est pas bonne comme prévu.

enter image description here

Ma classe de test est juste celle 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 base de mon application?

38
Patrick

Toutes ces réponses semblent exagérées.
Vous n’ajoutez pas de tests pour rendre un outil métrique heureux.
Le chargement d'un contexte Spring de l'application prend du temps . Ne l'ajoutez pas dans chaque version de développeur uniquement pour gagner environ 0,1% de couverture de votre application.
Ici vous ne couvrez pas 1 déclaration à partir de 1 méthode publique. Cela ne représente rien en termes de couverture dans une application où des milliers d'instructions sont généralement écrites .

Première solution de contournement: créez votre classe d'application Spring Boot sans bean déclaré à l'intérieur. Si vous les avez, déplacez-les dans une classe de configuration (pour les faire toujours couvrir par test unitaire). Et ensuite, ignorez votre classe d’application Spring Boot dans configuration de la couverture de test.

Deuxième solution de contournement: si vous devez vraiment couvrir l'invocation main() (pour des raisons d'organisation, par exemple), créez un test mais un test d'intégration (exécuté par un outil d'intégration continue et non dans chaque version de développeur) et documentez-le. clairement le but 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[] {});
   }
}
41
davidxxx

Vous pouvez faire quelque chose comme ça

@Test
public void applicationContextLoaded() {
}

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

J'avais le même objectif (avoir un test qui exécute la méthode main ()) et j'ai remarqué que le simple fait d'ajouter une méthode de test telle que @ fg78nc va en fait "démarrer" l'application deux fois: une fois par le cadre de test du démarrage initial, une fois via le invocation explicite de mainApp.main(new String[] {}), que je ne trouve pas élégante.

J'ai fini par écrire deux classes de test: une avec l'annotation @SpringBootTest et la méthode de test vide applicationContextLoaded () , une autre sans @SpringBootTest (uniquement 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[] {});
  }
}

Dans l’ensemble, l’application est toujours lancée deux fois, mais parce qu’il existe maintenant deux classes de test. Bien sûr, avec seulement ces deux méthodes de test, cela semble exagéré, mais d’autres tests seront ajoutés à la classe SpringBootApplicationTest en tirant parti de @SpringBootTest setup.

4
marcpa00
<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%, vous pouvez simplement ne pas avoir de méthode principale. Vous avez toujours besoin d'une classe annotée avec @SpringBootApplication mais elle peut être vide.

Soyez averti cependant car il a ses inconvénients et d’autres outils qui reposent sur main peuvent casser.

2
Mariano LEANCE

Vous pouvez simuler SpringApplication puisqu'il s'agit d'une dépendance de la méthode testée. Voyez comment ici . C'est à dire.

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
awgtek