web-dev-qa-db-fra.com

Remplacement de beans Spring Boot 2.1 par rapport à Primary

Avec le bean Spring Boot 2.1 est désactivé par défaut, ce qui est une bonne chose.

Cependant, j'ai quelques tests où je remplace les beans par des instances simulées utilisant Mockito. Avec le paramètre par défaut, les tests avec une telle configuration échoueront en raison du remplacement du bean.

La seule façon dont j'ai trouvé le travail, était d'activer le bean écrasant via les propriétés de l'application:

spring.main.allow-bean-definition-overriding=true

Cependant, j'aimerais vraiment que la configuration de définition de bean soit minimale pour ma configuration de test, ce qui serait signalé par printemps avec désactivation de la substitution.

Les haricots que je prends sont soit

  • Défini dans une autre configuration importée dans ma configuration de test
  • Bean découvert automatiquement par analyse d'annotation

Ce que je pensais devrait fonctionner dans la configuration de test en remplaçant le bean et en y insérant un @Primary, comme nous en avons l'habitude pour les configurations de source de données. Cela n'a cependant aucun effet et m'a amené à me demander: le @Primary et le haricot désactivé sont-ils contradictoires?

Quelques exemples:

package com.stackoverflow.foo;
@Service
public class AService {
}

package com.stackoverflow.foo;
public class BService {
}

package com.stackoverflow.foo;
@Configuration
public BaseConfiguration {
    @Bean
    @Lazy
    public BService bService() {
        return new BService();
    }
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean
    public BService bService() {
        return Mockito.mock(BService.class);
    }
}
19
hotzst

Remplacer les haricots signifie qu'il ne peut y avoir qu'un seul haricot avec un nom ou un identifiant unique dans le contexte. Ainsi, vous pouvez fournir deux haricots de la manière suivante:

package com.stackoverflow.foo;
@Configuration
public class BaseConfiguration {
   @Bean
   @Lazy
   public BService bService1() {
       return new BService();
   }
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean
    public BService bService2() {
        return Mockito.mock(BService.class);
    }
}

Si vous ajoutez @Primary, le bean primaire sera injecté par défaut dans:

@Autowired
BService bService;
7
Alien11689

spring.main.allow-bean-definition-overriding=true peut être placé dans des configurations de test. Si vous avez besoin de tests d'intégration approfondis, vous devrez éventuellement remplacer les beans. C'est inévitable.

Je voulais juste souligner une fois de plus que même si la réponse correcte a déjà été fournie, cela implique que votre bean aura des noms différents, donc techniquement, il ne s'agit pas d'une substitution. Le remplacement réel, si vous en avez besoin parce que vous utilisez @Qualifiers, @Resources ou quelque chose de similaire, à partir de Spring Boot 2.X n'est possible qu'avec spring.main.allow-bean-definition-overriding=true

Mise à jour: Soyez prudent avec DSL de définition de haricot Kotlin. Au démarrage de printemps, il faudra un ApplicationContextInitializer personnalisé, comme suit:

class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {

    override fun initialize(context: GenericApplicationContext) =
            beans.initialize(context)

}

Maintenant, si vous décidez de remplacer l'un de ces beans basés sur DSL dans votre test via la méthode @Primary @Bean, cela ne fonctionnera pas. L'initialiseur démarrera après les méthodes @Bean et vous obtiendrez toujours le bean initial basé sur DSL dans vos tests, même avec @Primary sur le test @Bean. Une autre option consisterait également à créer un initialiseur de test pour vos tests et à les répertorier dans vos propriétés de test, comme suit (l'ordre est important):

context:
    initializer:
        classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer

Bean Definition DSL prend également en charge la propriété primaire via:

bean(isPrimary=true) {...}

- dont vous aurez besoin pour éliminer toute ambiguïté lorsque vous essayez d’injecter un haricot, cependant main:allow-bean-definition-overriding: true n’est pas nécessaire si vous utilisez la méthode DSL pure.

(Spring Boot 2.1.3)

8
yuranos87

Il est permis de remplacer @Component par @Bean par défaut. Dans ton cas

@Service
public class AService {
}

@Component
public class BService {
    @Autowired
    public BService() { ... }
}

@Configuration
@ComponentScan
public BaseConfiguration {
}

@Configuration
// WARNING! Doesn't work with @SpringBootTest annotation
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean // you allowed to override @Component with @Bean.
    public BService bService() {
        return Mockito.mock(BService.class);
    }
}
1
Pavel