Injection de Mockito dans un haricot de printemps

284

Je voudrais injecter un objet maquette Mockito dans un bean Spring (3+) à des fins de tests unitaires avec JUnit. Mes dépendances de bean sont actuellement injectées en utilisant l' @Autowiredannotation sur les champs membres privés.

J'ai envisagé d'utiliser ReflectionTestUtils.setFieldmais l'instance de bean que je souhaite injecter est en fait un proxy et ne déclare donc pas les champs membres privés de la classe cible. Je ne souhaite pas créer un setter public pour la dépendance car je modifierai alors mon interface uniquement à des fins de test.

J'ai suivi quelques conseils donnés par la communauté Spring, mais la maquette n'est pas créée et le câblage automatique échoue:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

L'erreur que je rencontre actuellement est la suivante:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

Si je définit la constructor-argvaleur sur quelque chose de non valide, aucune erreur ne se produit lors du démarrage du contexte d'application.

théière
la source
4
Veuillez jeter un œil à cette toute petite créature: bitbucket.org/kubek2k/springockito/wiki/Home
kubek2k
C'est une approche très propre - j'aime ça!
teabot
2
Vous m'avez eu à Springockito-annotations.
yihtserns
2
Pour ceux qui utilisent le printemps 4. *, en janvier 2015, cela ne semble pas fonctionner avec la dernière version du printemps mockito et le projet semble être inactif.
Murali

Réponses:

130

La meilleure façon est:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

Mettre à jour
Dans le fichier de contexte, cette maquette doit être répertoriée avant tout champ autowired selon qu'elle est déclarée.

amra
la source
J'obtiens une erreur: "Erreur lors de la création du bean avec le nom 'mockito': la définition du bean est abstraite"
tttppp
4
@amra: le printemps ne déduit pas le type de l'objet retourné dans ce cas ... stackoverflow.com/q/6976421/306488
lisak
7
Je ne sais pas pourquoi cette réponse est tellement votée, le bean résultant ne peut pas être câblé automatiquement car il a le mauvais type.
azerole
4
Il peut être câblé automatiquement s'il est répertorié en premier dans le fichier de contexte (avant que les champs câblés automatiquement qui en dépendent soient déclarés.)
Ryan Walls
3
Depuis le printemps 3.2, l'ordre des haricots n'a plus d'importance. Voir la section intitulée "Generic Factory Methods" dans cet article de blog: spring.io/blog/2012/11/07/…
Ryan Walls
110
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

Cela injectera tous les objets simulés dans la classe de test. Dans ce cas, il injectera mockedObject dans testObject. Cela a été mentionné ci-dessus mais voici le code.

Greg Beauchamp
la source
1
Comment écraser une méthode particulière de mockedObject?
Jim Holden
@Teinacher when (mockedObject.execute) .thenReturn (objToReturn); Vous pouvez le mettre soit dans la méthode avant, soit dans votre méthode de test.
chaostheory
40
FYI: Cette approche ne fonctionnera pas, si je veux un câblage automatique partiel et une moquerie partielle dans MyTestObject.
raksja
9
Je ne sais pas pourquoi cela n'est pas voté plus haut. Si je vois plus de réponses contenant XML, je vais lancer.
MarkOfHall
3
Pourquoi ne l'utilisez-vous pas Mockito.spy(...)à la mockedObjectplace? Et puis utilisez when(mockedObject.execute).thenReturn(objToReturn)ou doReturn(objToReturn).when(mockedObject).execute(). La seconde n'invoque pas la vraie méthode. Vous pouvez également consulter la Mockito.doCallRealMethod()documentation
Tomasz Przybylski
63

J'ai une solution très simple utilisant Spring Java Config et Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}
Piotr Gwiazda
la source
4
Pour une raison quelconque avec cette approche, le printemps essaie de toute façon de créer le haricot réel (au lieu de la maquette) et s'étouffe avec ça ... Qu'est-ce que je fais mal?
Daniel Gruszczyk
1
J'ai le même problème
Korobko Alex
3
Ce n'est pas le printemps, mais plutôt le mockito qui essaie d'instancier un haricot réel si vous vous moquez d'une classe. Si vous avez des beans à moquer dans les tests, ils doivent être des implémentations d'une interface et injectés via cette interface. Si vous vous moquez ensuite de l'interface (plutôt que de la classe), mockito ne tentera pas d'instancier cette classe.
Daniel Gruszczyk
7
Dans quel but? Pourquoi ajouter les champs annotés et le constructeur avec initMocks? Pourquoi ne pas simplement return Mockito.mock(BeanA.class)en getBeanA? De cette façon, c'est plus simple et il y a moins de code. Qu'est-ce que je rate?
Oleg
1
@Oleg, il semble que vous ayez votre propre solution, que vous devriez probablement publier comme réponse, afin que la communauté puisse voter.
Dawood ibn Kareem
48

Donné:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

Vous pouvez charger la classe testée via le câblage automatique, simuler la dépendance avec Mockito, puis utiliser ReflectionTestUtils de Spring pour injecter la simulation dans la classe testée.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Veuillez noter qu'avant Spring 4.3.1, cette méthode ne fonctionnera pas avec les services derrière un proxy (annoté avec @Transactionalou Cacheable, par exemple). Cela a été corrigé par SPR-14050 .

Pour les versions antérieures, une solution consiste à déballer le proxy, comme décrit ici: l' annotation transactionnelle évite de se moquer des services (ce qui se ReflectionTestUtils.setFieldfait par défaut maintenant)

Paul Croarkin
la source
Double @RunWith (SpringJUnit4ClassRunner.class) et j'utilise des annotations différentes pour la classe de test (même coureur) mais cette approche fonctionne pour moi, merci.
user1317422
1
Je me suis beaucoup inspiré de "Veuillez noter qu'avant Spring 4.3.1, cette méthode ne fonctionnera pas avec les services derrière un proxy (annoté avec @Transactional ou Cacheable, par exemple). Cela a été corrigé par SPR-14050". Je suis juste tombé sur ce problème exactement et je n'ai obtenu aucun indice avant d'avoir repéré ces mots. MERCI BEAUCOUP!
snowfox
1
Cette solution gère lorsque vous avez connecté un contexte d'application entier et, à des fins de test, souhaite injecter une maquette dans un bean aléatoire dans votre contexte. J'ai utilisé cette réponse pour simuler un bean client simulé afin d'éviter les appels REST à d'autres modules dans un test de module. Je n'ai obtenu l'annotation InjectMock que lorsque vous injectez des maquettes dans un bean que vous êtes sur le point de tester, pas dans un bean créé par la configuration de l'application Spring.
Andreas Lundgren
1
Près d'une journée entière à essayer de faire fonctionner @MockBean sans réinitialiser le contexte, puis je tombe sur ce joyau. Exactement ce dont j'avais besoin, cheers.
Matt R
Fonctionne, bien que sachez que le champ remplacé peut ne pas être réinitialisé en raison de la mise en cache et que certains tests non liés peuvent échouer. Par exemple, dans mon test, j'ai remplacé l'encodeur de mot de passe par un faux et quelques autres tests se sont cassés en raison d'échecs d'autorisation.
alextsil
36

Si vous utilisez Spring Boot 1.4, il a une manière impressionnante de le faire. Utilisez simplement une nouvelle marque @SpringBootTestsur votre classe et @MockBeansur le terrain et Spring Boot créera une maquette de ce type et l'injectera dans le contexte (au lieu d'injecter l'original):

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

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

En revanche, si vous n'utilisez pas Spring Boot ou utilisez une version précédente, vous devrez faire un peu plus de travail:

Créez un @Configurationbean qui injecte vos mocks dans le contexte Spring:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

En utilisant @Primary annotation, vous dites au printemps que ce bean a la priorité si aucun qualificatif n'est spécifié.

Assurez-vous d'annoter la classe avec @Profile("useMocks")afin de contrôler quelles classes utiliseront la maquette et lesquelles utiliseront le vrai bean.

Enfin, dans votre test, activez le userMocksprofil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

Si vous ne voulez pas utiliser la maquette mais le vrai bean, n'activez pas le useMocksprofil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}
jfcorugedo
la source
5
Cette réponse devrait aller en haut - le support @MockBean dans Spring Boot peut également être utilisé sans Spring-Boot. Vous ne pouvez l'utiliser que dans les tests unitaires afin qu'il fonctionne pour toutes les applications de printemps!
bedrin
2
@Profile annotation que vous pouvez également définir sur la méthode de définition du bean, pour éviter de créer une classe de configuration distincte
marcin
Très bonne réponse! J'ai apporté quelques modifications pour le faire fonctionner avec ma web.xmlconfiguration old-school et AnnotationConfigWebApplicationContext. J'ai dû utiliser à la @WebAppConfigurationplace de @WebIntegrationTestet @ContextHierarchyavec @ContextConfigurationau lieu de @SpringApplicationConfiguration.
UTF_or_Death
J'ai dû ajouter l' @Primaryannotation pour mon cas, car il y avait un appel qui échouait dans un @PostConstructque je voulais simuler, mais le @PostConstructbean a été créé avant ma maquette, donc il n'a pas utilisé la maquette (jusqu'à ce que j'ajoute @Primary).
helleye
19

Depuis 1.8.3 Mockito a @InjectMocks- c'est incroyablement utile. Mes tests JUnit sont @RunWithles objets MockitoJUnitRunneret je construis @Mockqui satisfont toutes les dépendances de la classe testée, qui sont tous injectés lorsque le membre privé est annoté avec@InjectMocks .

Je @RunWithleSpringJUnit4Runner pour les tests d'intégration seulement maintenant.

Je noterai qu'il ne semble pas pouvoir s'injecter List<T>de la même manière que Spring. Il recherche uniquement un objet Mock qui satisfait à List, et n'injectera pas une liste d'objets Mock. La solution de contournement pour moi consistait à utiliser une @Spycontre une liste instanciée manuellement et à ajouter manuellement le ou les objets fictifs à cette liste pour les tests unitaires. Peut-être que c'était intentionnel, car cela m'a certainement forcé à porter une attention particulière à ce qui se moquait ensemble.

Doug Moscrop
la source
Oui, c'est la meilleure façon. Springockito n'injecte pas réellement les moqueries pour quelque raison que ce soit dans mon cas.
chaostheory
13

Mettre à jour: il existe désormais de meilleures solutions plus propres à ce problème. Veuillez d'abord considérer les autres réponses.

J'ai finalement trouvé une réponse à cette question par Ronen sur son blog. Le problème que je rencontrais est dû à la méthode Mockito.mock(Class c)déclarant un type de retour deObject . Par conséquent, Spring n'est pas en mesure de déduire le type de bean du type de retour de méthode d'usine.

La solution de Ronen consiste à créer une FactoryBeanimplémentation qui renvoie des simulations. leFactoryBean interface permet à Spring d'interroger le type d'objets créés par le bean d'usine.

Ma définition de bean simulé ressemble maintenant à:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
théière
la source
1
Lien mis à jour vers la solution de Ronen: narkisr.com/blog/2008/2647754885089732945
Jeff Martin
Je ne comprends pas cela, la méthode d'usine a un type de retour Object ... Mais la solution de l'amra a un type de retour générique afin que Spring le reconnaisse ... Mais la solution de l'amra ne fonctionne pas pour moi
lisak
Ni cette solution, spring ne déduit pas le type de bean qui est retourné par le factoryBean, donc pas de bean correspondant de type [com.package.Dao] ...
lisak
1
Machine de retour: web.archive.org/web/20120806223839/http://…
Daniel Kaplan
Ce lien fonctionne toujours: javadevelopmentforthemasses.blogspot.com/2008/07/… Désactivez simplement la redirection de lien dans votre navigateur et vous le verrez, au lieu d'être forcé de regarder le 404 sur son nouveau blog.
environ
12

Depuis le printemps 3.2, ce n'est plus un problème. Spring prend désormais en charge le câblage automatique des résultats des méthodes d'usine génériques. Voir la section intitulée "Méthodes génériques d'usine" dans cet article de blog: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .

Le point clé est:

Au printemps 3.2, les types de retour génériques pour les méthodes d'usine sont désormais correctement déduits, et le câblage automatique par type pour les simulations devrait fonctionner comme prévu. Par conséquent, des solutions de contournement personnalisées telles que MockitoFactoryBean, EasyMockFactoryBean ou Springockito ne sont probablement plus nécessaires.

Ce qui signifie que cela devrait fonctionner hors de la boîte:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
Ryan Walls
la source
9

Le code ci-dessous fonctionne avec le câblage automatique - ce n'est pas la version la plus courte mais utile quand il ne devrait fonctionner qu'avec des pots à ressort / mockito standard.

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 
Kamil
la source
A travaillé pour moi. J'ai dû déballer le proxy dans mon test afin de le vérifier comme décrit ici: forum.spring.io/forum/spring-projects/aop/…
Holgzn
9

Si vous utilisez spring> = 3.0 , essayez d'utiliser l' @Configurationannotation Springs pour définir une partie du contexte de l'application

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

Si vous ne souhaitez pas utiliser @ImportResource, cela peut aussi être fait dans l'autre sens:

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

Pour plus d'informations, consultez Spring-Framework-Reference: configuration de conteneur basée sur Java

Markus T
la source
Joli. Je l'ai utilisé lorsque le test que je teste est @Autowired dans le cas de test réel.
enkor
8

Ce n'est peut-être pas la solution parfaite, mais j'ai tendance à ne pas utiliser le ressort pour faire DI pour les tests unitaires. les dépendances pour un seul bean (la classe sous test) ne sont généralement pas trop complexes donc je fais juste l'injection directement dans le code de test.

Angelo Genovese
la source
3
Je comprends votre approche. Cependant, je me retrouve dans cette situation sur une grande base de code héritée qui ne permet pas facilement cela - pour le moment.
teabot
1
J'ai trouvé le combo Mockito / Spring très utile lorsque j'ai besoin de tester du code qui dépend fortement des aspects Spring / AOP (par exemple, lors du test des règles de sécurité de Spring). Bien que l'on soit parfaitement justifié de prétendre que de tels tests devraient être un test d'intégration.
Lars Tackmann
@Lars - d'accord - on pourrait en dire autant des tests auxquels je fais face.
teabot
7

Je peux faire ce qui suit en utilisant Mockito:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>
Alexandre
la source
1
Merci pour la réponse @Alexander. Puis-je demander: est-ce que le câblage est correct? Si oui, quelles versions de Spring / Mockito utilisez-vous?
teabot
6

Affichage de quelques exemples basés sur les approches ci-dessus

Avec le printemps:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

Sans ressort:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}
Basu
la source
2

Mise à jour - nouvelle réponse ici: https://stackoverflow.com/a/19454282/411229 . Cette réponse ne s'applique qu'à ceux des versions Spring antérieures à 3.2.

J'ai cherché pendant un certain temps une solution plus définitive à cela. Ce billet de blog semble couvrir tous mes besoins et ne repose pas sur la commande de déclarations de bean. Tous les crédits à Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/

Fondamentalement, implémentez un FactoryBean

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

Mettez à jour votre configuration de printemps avec les éléments suivants:

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>
Ryan Walls
la source
2

En regardant le rythme de développement de Springockito et le nombre de problèmes en suspens , je serais un peu inquiet de l'introduire dans ma pile de suite de tests de nos jours. Le fait que la dernière version ait été réalisée avant la version Spring 4 soulève des questions comme "Est-il possible de l'intégrer facilement à Spring 4?". Je ne sais pas, parce que je ne l'ai pas essayé. Je préfère l'approche Spring pure si j'ai besoin de se moquer de Spring Bean dans le test d'intégration.

Il existe une option pour simuler le haricot de printemps avec des fonctionnalités de printemps simples. Vous devez l'utiliser @Primary, @Profileet des @ActiveProfilesannotations pour cela. J'ai écrit un article de blog sur le sujet.

luboskrnac
la source
1

J'ai trouvé une réponse similaire à celle du teabot pour créer une MockFactory qui fournit les simulacres. J'ai utilisé l'exemple suivant pour créer la fausse usine (car le lien vers narkisr est mort): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

Cela permet également d'éviter que Spring veuille résoudre les injections du haricot moqué.

Renso Lohuis
la source
1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

cela ^ fonctionne parfaitement bien s'il est déclaré en premier / au début du fichier XML. Mockito 1.9.0 / Spring 3.0.5

amande rose
la source
1

J'utilise une combinaison de l'approche utilisée en réponse par Markus T et une implémentation d'aide simple ImportBeanDefinitionRegistrarqui recherche une annotation personnalisée (@MockedBeans ) dans laquelle on peut spécifier quelles classes doivent être moquées. Je crois que cette approche se traduit par un test unitaire concis avec une partie du code passe-partout lié à la moquerie supprimée.

Voici à quoi ressemble un exemple de test unitaire avec cette approche:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

Pour ce faire, vous devez définir deux classes d'assistance simples - une annotation personnalisée ( @MockedBeans) et une ImportBeanDefinitionRegistrarimplémentation personnalisée . @MockedBeansla définition d'annotation doit être annotée @Import(CustomImportBeanDefinitionRegistrar.class)et la ImportBeanDefinitionRgistrarnécessité d'ajouter des définitions de beans fictifs à la configuration dans sa registerBeanDefinitionsméthode.

Si vous aimez l'approche, vous pouvez trouver des exemples d' implémentations sur mon blogpost .

Krešimir Nesek
la source
1

J'ai développé une solution basée sur la proposition de Kresimir Nesek. J'ai ajouté une nouvelle annotation @EnableMockedBean afin de rendre le code un peu plus propre et modulaire.

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

J'ai écrit un article pour l' expliquer.

Alfredo Diaz
la source
1

Je suggère de migrer votre projet vers Spring Boot 1.4. Après cela, vous pouvez utiliser une nouvelle annotation @MockBeanpour simuler votrecom.package.Dao

luboskrnac
la source
0

Aujourd'hui, j'ai découvert qu'un contexte printanier où je déclarais un avant les haricots Mockito ne se chargeait pas. Après avoir déplacé APRÈS les simulations, le contexte de l'application a été chargé avec succès. Prends soin de toi :)

Daniele Dellafiore
la source
1
Il manque quelque chose. 8-) Vous avez déplacé quoi après les moqueries?
Hans-Peter Störr
0

Pour mémoire, tous mes tests fonctionnent correctement en faisant juste l'initialisation paresseux du projecteur, par exemple:

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

Je suppose que la justification est celle que Mattias explique ici (au bas du message), qu'une solution de contournement change l'ordre dans lequel les beans sont déclarés - l'initialisation paresseuse est une sorte de "déclaration" du luminaire à la fin.

Fabrizio Giudici
la source
-1

Si vous utilisez l'injection de contrôleur, assurez-vous que vos variables locales ne sont PAS "finales"

RS
la source