Injection de Mockito Mock dans un haricot

j'aimerais injecter un objet Mockito mock dans un haricot à ressort (3+) pour les besoins de l'essai à l'unité avec JUnit. Mes dépendances de haricot sont actuellement injectées en utilisant l'annotation @Autowired sur les champs membres privés.

j'ai envisagé d'utiliser ReflectionTestUtils.setField mais l'instance fève 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 veux pas créer un setter public à la dépendance comme je vais alors modifier mon interface purement pour les fins de test.

j'ai suivi quelques conseil donné par la communauté de Spring mais la simulation ne se crée pas et l'auto-câblage é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 mets la valeur constructor-arg à quelque chose d'invalide, aucune erreur ne se produit lors du démarrage du contexte de l'application.

249
demandé sur teabot 2010-03-16 21:58:07

23 réponses

La meilleure façon est:

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

mise à Jour

Dans le contexte d'un fichier cette maquette doit figurer avant toute autocâblés champ selon elle est déclarée.

117
répondu amra 2013-08-22 09:37:00
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

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

ceci injectera tout objet moqué dans la classe test. Dans ce cas, il injectera mockedObject dans le testObject. Cela a été mentionné ci-dessus, mais voici le code.

99
répondu Greg Beauchamp 2018-01-23 22:50:05

j'ai une solution très simple en utilisant la config Java de printemps 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;
    }
}
59
répondu Piotr Gwiazda 2012-08-20 13:26:22

:

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

    // etc
}

vous pouvez faire charger la classe testée par autowiring, simuler la dépendance avec Mockito, puis utiliser les Réflectiontestutils du ressort 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 le printemps 4.3.1, cette méthode ne fonctionnera pas avec les services derrière un proxy (annoté avec @Transactional , ou Cacheable , par exemple). Ceci a été fixé par SPR-14050 .

pour les versions précédentes, une solution consiste à déballer le proxy, comme décrit ici: l'annotation transactionnelle évite de se moquer des services (ce que ReflectionTestUtils.setField fait par défaut maintenant)

38
répondu Paul Croarkin 2018-03-24 20:12:32

si vous utilisez la botte de printemps 1.4, il a une façon impressionnante de le faire. Il suffit d'utiliser la nouvelle marque @SpringBootTest sur votre classe et @MockBean sur le terrain et la botte à ressort créera un mock de ce type et il 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");
    }

}

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

créer un haricot @Configuration qui injecte vos moqueries dans le contexte du printemps:

@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 haricot a la priorité si aucun qualificatif n'est spécifié.

assurez-vous d'annoter la classe avec @Profile("useMocks") afin de contrôler les classes qui utiliseront la simulation et celles qui utiliseront la vraie fève.

enfin, dans votre test, activez userMocks profil:

@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 le mock mais le vrai bean, il suffit de ne pas activer useMocks profil:

@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() {
        ....
    }
}
31
répondu jfcorugedo 2016-09-28 13:14:36

depuis 1.8.3 Mockito a @InjectMocks-c'est incroyablement utile. Mes tests JUnit sont @RunWith the MockitoJUnitRunner et je construis des objets @ Mock qui satisfont toutes les dépendances de la classe testée, qui sont tous injectés lorsque le membre privé est annoté avec @InjectMocks.

je annotation @RunWith la SpringJUnit4Runner pour les tests d'intégration seulement maintenant.

je note qu'il ne semble pas être en mesure d'injecter Liste de la même manière que le Printemps. Il recherche seulement un objet Mock qui satisfait à la liste, et n'injectera pas une liste d'objets Mock. La solution pour moi était d'utiliser un @Spy contre une liste instanciée manuellement, et manuellement .ajouter le simulacre de l'objet(s) à cette liste pour les tests unitaires. Peut-être que c'était intentionnel, parce que ça m'a forcé à faire très attention à ce qu'on se moquait ensemble.

19
répondu Doug Moscrop 2011-03-07 16:16:48

mise à Jour: Il y a mieux maintenant, plus propres solutions à ce problème. Veuillez d'abord considérer les autres réponses.

j'ai finalement trouvé une réponse à cela par ronen sur son blog. Le problème que j'avais était dû à la méthode Mockito.mock(Class c) déclarant un type de retour de Object . Par conséquent le ressort ne peut pas inférer le type de haricot du type de retour de la méthode d'usine.

Ronen la solution est pour créer une implémentation FactoryBean qui renvoie les moqueries. L'interface FactoryBean permet à Spring d'interroger le type d'objets créés par la fève d'usine.

Mon moqué de haricot définition ressemble maintenant à:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
13
répondu teabot 2011-07-25 09:44:39

à compter du printemps 3.2, cette question ne se pose plus. Spring prend maintenant en charge L'Autowiring des résultats des méthodes génériques d'usine. Voir la section intitulée "méthodes génériques D'usine" dans ce billet 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 génériques de retour pour les méthodes d'usine sont maintenant correctement déduit, et l'autodiagnostic par type pour les moqueurs devrait fonctionner comme suit: devrait. En conséquence, des solutions de rechange personnalisées telles que MockitoFactoryBean, EasyMockFactoryBean, ou Springockito sont probablement pas plus nécessaire.

ce qui veut dire que cela devrait être dans la boîte:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
11
répondu Ryan Walls 2013-10-18 16:13:57

ci - dessous code fonctionne avec autowiring-il n'est pas la version la plus courte mais utile quand il devrait fonctionner seulement avec ressort standard/jars mockito.

<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> 
9
répondu Kamil 2015-08-07 02:13:46

si vous utilisez spring > = 3.0 , essayez D'utiliser @Configuration annotation 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 voulez pas utiliser la source @ImportResource, il peut être fait dans l'autre sens aussi:

<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-référence: Java-based container configuration

9
répondu Markus T 2018-03-24 19:52:26

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>
7
répondu Alexander 2014-10-19 21:33:04

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 haricot (la classe à l'essai) ne sont généralement pas trop complexes donc je fais juste l'injection directement dans le code de test.

6
répondu Angelo Genovese 2010-03-16 19:02:37

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

Avec Ressort:

@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;
}
6
répondu Basu 2012-09-10 13:02:04

Update - nouvelle réponse ici: https://stackoverflow.com/a/19454282/411229 . Cette réponse ne s'applique qu'aux versions à ressort antérieures à 3.2.

j'ai cherché pendant un certain temps une solution plus définitive à cela. Ce blog semble couvrir tous mes besoins, et ne pas compter sur la commande de haricot déclarations. Tout le mérite revient à Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects /

essentiellement, mettre en œuvre 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;
    }
}

mettre à jour votre configuration de printemps avec ce qui suit:

<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>
2
répondu Ryan Walls 2017-05-23 12:02:53

en regardant Springockito rythme de développement et nombre de questions ouvertes , je serais peu inquiet de l'introduire dans ma pile de suite de nos jours. Le fait que la dernière version ait été faite avant la version du printemps 4 soulève des questions comme "est-il possible de l'intégrer facilement avec le printemps 4?". Je ne sais pas, parce que je ne l'ai pas essayé. Je préfère une approche purement printanière si j'ai besoin de simuler un test d'intégration.

Il ya une option de faux haricot à ressort avec des caractéristiques de ressort simple. Vous devez utiliser @Primary , @Profile et @ActiveProfiles annotations. j'ai écrit un billet de blog sur le sujet.

2
répondu luboskrnac 2016-02-29 20:16:51

j'ai trouvé une réponse similaire à teabot pour créer un MockFactory qui fournit les moqueries. J'ai utilisé l'exemple suivant pour créer la fausse usine (puisque 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 aide aussi à prévenir que le ressort veut résoudre les injections de la moqué de haricot.

1
répondu Renso Lohuis 2012-02-29 13:09:38
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

ce ^ fonctionne parfaitement bien si déclarée d'abord/au début du fichier XML. Mockito 1.9.0 / Printemps 3.0.5

1
répondu almondandapricot 2012-12-23 16:19:54

j'utilise une combinaison de l'approche utilisée dans answer by Markust et un simple helper implémentation de ImportBeanDefinitionRegistrar qui cherche une annotation personnalisée ( @MockedBeans ) dans laquelle on peut spécifier quelles classes doivent être moquées. Je crois que cette approche résulte en un test unitaire concis avec une partie du code boilerplate lié à la moquerie enlevé.

voici à quoi ressemble un test d'unité d'échantillonnage 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 faire ce vous devez définir deux classes d'aide simples-l'annotation personnalisée ( @MockedBeans ) et un ImportBeanDefinitionRegistrar la mise en œuvre. La définition de l'annotation @MockedBeans doit être annotée par @Import(CustomImportBeanDefinitionRegistrar.class) et la définition de ImportBeanDefinitionRgistrar doit être ajoutée à la configuration de la méthode registerBeanDefinitions .

si vous aimez l'approche, vous pouvez trouver l'échantillon implémentations sur mon blogpost .

1
répondu Kresimir Nesek 2015-06-06 10:50:06

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 post en l'expliquant.

1
répondu Alfredo Diaz 2015-06-06 20:00:55

je suggère de migrer votre projet vers la botte de printemps 1.4. Après cela, vous pouvez utiliser la nouvelle annotation @MockBean pour simuler votre com.package.Dao

1
répondu luboskrnac 2016-09-05 13:10:30

Aujourd'hui j'ai découvert qu'un contexte de printemps où j'ai déclaré un avant les haricots Mockito, ne se chargeait pas. Après avoir déplacé L'après-Mock, le contexte app a été chargé avec succès. Prendre soin :)

0
répondu Daniele Dellafiore 2011-04-27 19:54:46

pour mémoire, tous mes tests fonctionnent correctement en rendant le montage paresseux-initialisé, 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 le raisonnement est celui Mattias explique ici (au bas du poteau), qu'une solution de contournement est en train de changer l'ordre dans lequel les haricots sont déclarés - l'initialisation paresseuse est" en quelque sorte " avoir le montage déclaré à la fin.

0
répondu Fabrizio Giudici 2013-07-04 23:06:47

si vous utilisez L'Injection du contrôleur, assurez-vous que vos variables locales ne sont pas" finales "

-1
répondu R.S 2017-05-04 12:07:36