web-dev-qa-db-fra.com

Configurer le composant @MockBean avant le démarrage de l'application

J'ai une application Spring Boot 1.4.2. Un code utilisé au démarrage ressemble à ceci:

@Component 
class SystemTypeDetector{
    public enum SystemType{ TYPE_A, TYPE_B, TYPE_C }
    public SystemType getSystemType(){ return ... }
}

@Component 
public class SomeOtherComponent{
    @Autowired 
    private SystemTypeDetector systemTypeDetector;
    @PostConstruct 
    public void startup(){
        switch(systemTypeDetector.getSystemType()){   // <-- NPE here in test
        case TYPE_A: ...
        case TYPE_B: ...
        case TYPE_C: ...
        }
    }
}

Il existe un composant qui détermine le type de système. Ce composant est utilisé lors du démarrage à partir d'autres composants. En production, tout fonctionne bien.

Maintenant, je veux ajouter des tests d'intégration en utilisant @MockBean De Spring 1.4.

Le test ressemble à ceci:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
    @MockBean 
    private SystemTypeDetector systemTypeDetectorMock;

    @Before 
    public void initMock(){
       Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
    }

    @Test 
    public void testNrOne(){
      // ...
    }
}

Fondamentalement, la moquerie fonctionne bien. Mon systemTypeDetectorMock est utilisé et si j'appelle getSystemType -> TYPE_C Est retourné.

Le problème est que l'application ne démarre pas. Actuellement, le bon fonctionnement semble être:

  1. créer tous les Mocks (sans configuration, toutes les méthodes renvoient null)
  2. démarrer l'application
  3. call @ Before-methods (où les mocks seraient configurés)
  4. commencer le test

Mon problème est que l'application démarre avec une maquette non initialisée. L'appel à getSystemType() renvoie donc null.

Ma question est: comment puis-je configurer les maquettes avant le démarrage de l'application?

Edit: Si quelqu'un a le même problème, une solution de contournement consiste à utiliser @MockBean(answer = CALLS_REAL_METHODS). Cela appelle le vrai composant et dans mon cas, le système démarre. Après le démarrage, je peux changer le comportement factice.

24
Marcel

Dans ce cas, vous devez configurer les maquettes d'une manière que nous avions l'habitude de faire avant @MockBean a été introduit - en spécifiant manuellement un @Primary bean qui remplacera l'original dans le contexte.

@SpringBootTest
class DemoApplicationTests {

    @TestConfiguration
    public static class TestConfig {

        @Bean
        @Primary
        public SystemTypeDetector mockSystemTypeDetector() {
            SystemTypeDetector std = mock(SystemTypeDetector.class);
            when(std.getSystemType()).thenReturn(TYPE_C);
            return std;
        }

    }

    @Autowired
    private SystemTypeDetector systemTypeDetector;

    @Test
    void contextLoads() {
        assertThat(systemTypeDetector.getSystemType()).isEqualTo(TYPE_C);
    }
}

Puisque @TestConfiguration classe est une classe interne statique, elle ne sera choisie automatiquement que par ce test. Comportement simulé complet que vous mettriez dans @Before doit être déplacé vers une méthode qui initialise un bean.

7
Maciej Walkowiak

Ce que vous utilisez est bon pour un test unitaire:

org.mockito.Mockito#when()

Essayez d'utiliser les méthodes suivantes pour vous moquer des haricots de printemps lorsque le contexte est défini:

org.mockito.BDDMockito#given()

Si vous utilisez @SpyBean, alors vous devez utiliser une autre syntaxe:

willReturn(Arrays.asList(val1, val2))
        .given(service).getEntities(any());
0
Niki.Max

L'initialisation de Spring est déclenchée avant @Before L'annotation de Mockito pour que la maquette ne soit pas initialisée au moment où le @PostConstruct la méthode annotée est exécutée.

Essayez de "retarder" la détection de votre système à l'aide de @Lazy annotation sur le composant SystemTypeDetector. Utilisez votre SystemTypeDetector là où vous en avez besoin, gardez à l'esprit que vous ne pouvez pas déclencher cette détection dans un @PostConstruct ou crochet équivalent.

0
Florian Lopes