Injecting Mockito mocks into a Spring bean
Asked Answered
D

25

304

I would like to inject a Mockito mock object into a Spring (3+) bean for the purposes of unit testing with JUnit. My bean dependencies are currently injected by using the @Autowired annotation on private member fields.

I have considered using ReflectionTestUtils.setField but the bean instance that I wish to inject is actually a proxy and hence does not declare the private member fields of the target class. I do not wish to create a public setter to the dependency as I will then be modifying my interface purely for the purposes of testing.

I have followed some advice given by the Spring community but the mock does not get created and the auto-wiring fails:

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

The error I currently encounter is as follows:

...
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)

If I set the constructor-arg value to something invalid no error occurs when starting the application context.

Doriedorin answered 16/3, 2010 at 18:58 Comment(8)
Please take a look at this tiny little creature: bitbucket.org/kubek2k/springockito/wiki/HomeEnvoy
This is a very clean approach - I like it!Doriedorin
You had me at Springockito-annotations.Exteriorize
Forget all the rest, springockito-annotations is something you want to use. The nice thing is that you can have a non-complete XML configuration (omit the mocks) and annotations will fill in the blanks (the mocks). Then just @Autowire everything.Mouseear
Sadly, Springockito doesn't let you easily grab the WebApplicationContext (you might do this if you're writing an integration test with @WebAppConfiguation), as documented by tihs issue: bitbucket.org/kubek2k/springockito/issue/12/… You've probably just got to call the mock() method yourself.Kelcy
Beware of bitbucket.org/kubek2k/springockito/issue/37/…Hollandia
For those using spring 4.*, as of Jan 2015 this does not appear to be working with latest spring mockito version and the project appears to be inactive.Apparition
@kubek2k: your links are broken.. could you please update it?Simonize
T
134

The best way is:

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

Update
In the context file this mock must be listed before any autowired field depending on it is declared.

Tovatovar answered 18/8, 2010 at 14:6 Comment(12)
I get an error: "Error creating bean with name 'mockito': bean definition is abstract"Frostbite
@amra: spring dosn't infer the type of the object returned in this case... https://mcmap.net/q/101637/-mocking-spring-beans/306488Shores
Don't know why this answer is upvoted so much, the resulting bean cannot be autowired because it has the wrong type.Syllabic
It can be autowired if it is listed first in the context file (before any autowired fields that would depend on it are declared.)Pantry
Agree with @Ryan, it works if the mock bean is defined before the dependent bean. I think this is because Spring unable to guess what the type of the mocked bean would be, hence unless it is actually there, Spring unable to determine that the mock has to be created beforehandCommoner
Yup, as @Ryan said, works for us, when the mock bean is configured at the top of the context file (after any imports).Kenrick
As of spring 3.2, the order of the beans no longer matters. See the section entitled "Generic Factory Methods" in this blog post: spring.io/blog/2012/11/07/…Pantry
See also #16833563 about spring 3.2 remedy.Hollandia
The question says that the OP has tried this and it didn't work. There's no point in having an answer that just repeats what the OP is already doing. Did over 100 upvoters bother to read the question?Dyeline
This doesn't work for me... I get: org.springframework.beans.factory.BeanNotOfRequiredTypeExceptionSpotweld
I want to have a spy not mock. Can I do this in xml config?Springhalt
Use factory-method="spy".Tovatovar
C
118
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

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

This will inject any mocked objects into the test class. In this case it will inject mockedObject into the testObject. This was mentioned above but here is the code.

Circumstance answered 5/1, 2012 at 12:49 Comment(8)
How do I stub a particular method of mockedObject?Oscilloscope
@Teinacher when(mockedObject.execute).thenReturn(objToReturn); You can put that either in the before method or inside your test method.Onondaga
FYI: This approach won't work, if i want partial Autowiring and partial mocking in MyTestObject.Mccue
This is exactly the type of solution I was looking for. Very simple and elegant, and gets the job done. Not sure why there aren't more up votes.Anderaanderea
I don't know why this isn't voted higher. If I see any more answers containing XML I'm going to hurl.Pinion
Worth adding, if you want to do partial Autowiring and partial mocking (as mentioned by @techastute), you could just drop the "@Injectmocks" annotation and inject your dependencies (mocked or otherwise) via a constructor. You would of course have to annotate the new constructor with "@Autowired".Olympe
Why don't you use Mockito.spy(...) on this mockedObject instead? And then use when(mockedObject.execute).thenReturn(objToReturn) or doReturn(objToReturn).when(mockedObject).execute(). Second one don't invoke real method. You can check also Mockito.doCallRealMethod() documentationBivins
If you want partial autowiring, use spy instead.Chemisorption
D
65

I have a very simple solution using Spring Java Config and 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;
    }
}
Dorn answered 20/8, 2012 at 13:26 Comment(8)
Thank you Piotr! This works beautifully - you deserve 10,000 upvotes for this, and I am sure they will sing songs about you in heaven.Deuce
For some reason with this approach, spring tries to create the actual bean anyway (instead of mock) and chokes on that... What am I doing wrong?Supraorbital
I have the same issuePl
Not spring but rather mockito tries to instantiate an actual bean if you are mocking a class. If you have any beans that have to be mocked in tests, they should be implementations of an interface, and injected via that interface. If you then mock the interface (rather than class), mockito won't attempt to instantiate that class.Supraorbital
I get No qualifying bean of type [class name] is defined: expected single matching bean but found 2:Afflictive
Try @Primary when declaring mock beanSolvent
What is the point? Why add the annotated fields and constructor with initMocks? Why not just return Mockito.mock(BeanA.class) in getBeanA ? This way it's simpler and there is less code. What am I missing?Immigration
@Immigration it sounds like you have your own solution, which you should probably post as an answer, so that the community can vote on it.Dyeline
H
56

Given:

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

    // etc
}

You can have the class that is being tested loaded via autowiring, mock the dependency with Mockito, and then use Spring's ReflectionTestUtils to inject the mock into the class being tested.

@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
}

Please note that before Spring 4.3.1, this method won't work with services behind a proxy (annotated with @Transactional, or Cacheable, for example). This has been fixed by SPR-14050.

For earlier versions, a solution is to unwrap the proxy, as described there: Transactional annotation avoids services being mocked (which is what ReflectionTestUtils.setField does by default now)

Halfpint answered 27/3, 2014 at 21:18 Comment(6)
Double @RunWith(SpringJUnit4ClassRunner.class) and I use different annotations for test class (same runner) but this approach works for me, thanks.Lucent
I was inspired a lot by "Please note that before Spring 4.3.1, this method won't work with services behind a proxy (annotated with @Transactional, or Cacheable, for example). This has been fixed by SPR-14050" . I just ran into this issue exactly and didn't get any clue until spotting this words. THANKS A LOT!Cecilla
This solution handles when you have wired up an entire application context and, for test purpose, wants to inject a mock in a random bean in your context. I used this answer to mock a feign client bean in order to avoid REST calls to other modules in a Module test. I only got the InjectMock annotation to work when you are injecting mocks in a bean that you are about to test, not in a bean created by the Spring Application Configuration.Accumulate
Nearly a whole day fluffing around trying to get @MockBean working without resetting the context and then I come across this gem. Exactly what I needed, cheers.Pyrogenic
Works, although beware that the replaced field might not reset due to caching and some unrelated tests could break. E.g. in my test i replaced the password encoder with a mock one and a few other tests broke due to authorization failures.Mcgehee
@MattR: you can use @MockInBean as an alternative to @MockBean that does not reset the context and keep it clean. See my answer.Shroff
C
55

If you're using Spring Boot 1.4, it has an awesome way of doing this. Just use new brand @SpringBootTest on your class and @MockBean on the field and Spring Boot will create a mock of this type and it will inject it into the context (instead of injecting the original one):

@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");
    }

}

On the other hand, if you're not using Spring Boot or are you using a previous version, you'll have to do a bit more work:

Create a @Configuration bean that injects your mocks into Spring context:

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

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

Using @Primary annotation you're telling spring that this bean has priority if no qualifier are specified.

Make sure you annotate the class with @Profile("useMocks") in order to control which classes will use the mock and which ones will use the real bean.

Finally, in your test, activate userMocks profile:

@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() {
        ....
    }
}

If you don't want to use the mock but the real bean, just don't activate useMocks profile:

@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() {
        ....
    }
}
Christcrossrow answered 8/10, 2015 at 13:43 Comment(6)
This answer should go to the top - @MockBean support in spring boot also can be used without spring-boot. You can use it in unit tests only so it works for all spring applications!Anglim
@Profile annotation you can set also on the bean definition method, to avoid creating separate configuration classEvenson
Great answer! I made a few changes to make it work with my old-school web.xml and AnnotationConfigWebApplicationContext setup. Had to use @WebAppConfiguration instead of @WebIntegrationTest and @ContextHierarchy with @ContextConfiguration instead of @SpringApplicationConfiguration.Frederic
I had to add the @Primary annotation for my case, since there was a failing call inside a @PostConstruct that I wanted to mock, but the @PostConstruct's bean was created before my mock so it didn't use the mock (until I added @Primary).Hengelo
@MockBean has a significant drawback: it may lead to the re-creation of your Spring context in your build test phase. (See #45587713 or the problem with @MockBean). You can use @MockInBean as an alternative to @MockBean that does not reset the context. See my answerShroff
Great solution, also works with partial mocks (i.e. when you want to mock one bean, but leave the rest of the dependencies as-is).Delphinium
G
20

Since 1.8.3 Mockito has @InjectMocks - this is incredibly useful. My JUnit tests are @RunWith the MockitoJUnitRunner and I build @Mock objects that satisfy all the dependencies for the class being tested, which are all injected when the private member is annotated with @InjectMocks.

I @RunWith the SpringJUnit4Runner for integration tests only now.

I will note that it does not seem to be able to inject List<T> in the same manner as Spring. It looks only for a Mock object that satisfies the List, and will not inject a list of Mock objects. The workaround for me was to use a @Spy against a manually instantiated list, and manually .add the mock object(s) to that list for unit testing. Maybe that was intentional, because it certainly forced me to pay close attention to what was being mocked together.

Guillory answered 7/3, 2011 at 16:16 Comment(1)
Yeah this is the best way. Springockito doesn't actually inject the mocks for whatever reason in my case.Onondaga
D
14

Update: There are now better, cleaner solutions to this problem. Please consider the other answers first.

I eventually found an answer to this by ronen on his blog. The problem I was having is due to the method Mockito.mock(Class c) declaring a return type of Object. Consequently Spring is unable to infer the bean type from the factory method return type.

Ronen's solution is to create a FactoryBean implementation that returns mocks. The FactoryBean interface allows Spring to query the type of objects created by the factory bean.

My mocked bean definition now looks like:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
Doriedorin answered 17/3, 2010 at 10:18 Comment(6)
Updated link to Ronen's Solution: narkisr.com/blog/2008/2647754885089732945Gerik
I don't understand that, the factory method has return type Object ... But the amra's solution has a generic return type so that Spring should recognize it... But the amra's solution doesn't work for meShores
Neither this solution, spring doesn't infer the type of bean that is returned from the factoryBean hence No matching bean of type [ com.package.Dao ] ...Shores
Way back machine: web.archive.org/web/20120806223839/http://…Patsy
This link actually still works: javadevelopmentforthemasses.blogspot.com/2008/07/… Just disable link redirection in your browser and you'll see it, instead of being force to look at the 404 on his new blog.Sniffle
Or do what I did. Visit the original link - then you've got about 0.2 seconds before the content disappears and is replaced with the 404 message from the new blog. That's just long enough to press Ctrl-A Ctrl-C to copy the content. Then paste it into a text editor to read.Dyeline
P
12

As of Spring 3.2, this is no longer an issue. Spring now supports Autowiring of the results of generic factory methods. See the section entitled "Generic Factory Methods" in this blog post: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/.

The key point is:

In Spring 3.2, generic return types for factory methods are now properly inferred, and autowiring by type for mocks should work as expected. As a result, custom work-arounds such as a MockitoFactoryBean, EasyMockFactoryBean, or Springockito are likely no longer necessary.

Which means this should work out of the box:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
Pantry answered 18/10, 2013 at 16:13 Comment(0)
D
10

If you're using spring >= 3.0, try using Springs @Configuration annotation to define part of the application context

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

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

}

If you don't want to use the @ImportResource, it can be done the other way around too:

<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>

For more information, have a look at spring-framework-reference : Java-based container configuration

Deformity answered 20/7, 2011 at 13:21 Comment(1)
Nice one. I used this when the test I am testing is @Autowired in the actual test case.Cenacle
N
9

Below code works with autowiring - it is not the shortest version but useful when it should work only with standard spring/mockito jars.

<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> 
Nanon answered 28/9, 2011 at 12:27 Comment(1)
Worked for me. I had to unwrap the proxy in my test in order to verify it as described here: forum.spring.io/forum/spring-projects/aop/…Rigobertorigor
C
8

Perhaps not the perfect solution, but I tend not to use spring to do DI for unit tests. the dependencies for a single bean (the class under test) usually aren't overly complex so I just do the injection directly in the test code.

Colostomy answered 16/3, 2010 at 19:2 Comment(3)
I understand your approach. However, I find myself in this situation on a large legacy code base that doesn't easily allow for this - yet.Doriedorin
I have found the Mockito/Spring combo to be very useful when I need to test code that depends heavily on Spring aspects/AOP (for instance, when testing spring security rules). Although one is perfectly justified in claiming that such tests should be a integration test.Conatus
@Lars - agreed - the same could be said of the tests I am dealing with.Doriedorin
E
7

I can do the following using Mockito:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>
Emersed answered 26/4, 2010 at 9:16 Comment(2)
Thanks for the answer @Alexander. May I ask: does it wire-up correctly? If so which versions of Spring/Mockito are you using?Doriedorin
I want a spy not a mock. How to do that ?Springhalt
S
6

Posting a few examples based on the above approaches

With Spring:

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

Without Spring:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}
Sachi answered 10/9, 2012 at 13:2 Comment(0)
P
2

Update - new answer here: https://mcmap.net/q/99575/-injecting-mockito-mocks-into-a-spring-bean. This answer only applies to those on Spring versions before 3.2.

I've looked for a while for a more definitive solution to this. This blog post seems to cover all my needs and doesn't rely on ordering of bean declarations. All credit to Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/

Basically, implement a 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;
    }
}

Next update your spring config with the following:

<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>
Pantry answered 9/7, 2013 at 23:14 Comment(0)
E
2

Looking at Springockito pace of development and number of open issues, I would be little bit worried to introduce it into my test suite stack nowadays. Fact that last release was done before Spring 4 release brings up questions like "Is it possible to easily integrate it with Spring 4?". I don't know, because I didn't try it. I prefer pure Spring approach if I need to mock Spring bean in integration test.

There is an option to fake Spring bean with just plain Spring features. You need to use @Primary, @Profile and @ActiveProfiles annotations for it. I wrote a blog post on the topic.

Exsect answered 7/12, 2014 at 20:41 Comment(2)
Blog post link 404sMoyer
Sorry there was mistake in links numbering, fixed now.Exsect
R
2

I use a combination of the approach used in answer by Markus T and a simple helper implementation of ImportBeanDefinitionRegistrar that looks for a custom annotation (@MockedBeans) in which one can specify which classes are to be mocked. I believe that this approach results in a concise unit test with some of the boilerplate code related to mocking removed.

Here's how a sample unit test looks with that approach:

@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
        }
    }
}

To make this happen you need to define two simple helper classes - custom annotation (@MockedBeans) and a custom ImportBeanDefinitionRegistrar implementation. @MockedBeans annotation definition needs to be annotated with @Import(CustomImportBeanDefinitionRegistrar.class) and the ImportBeanDefinitionRgistrar needs to add mocked beans definitions to the configuration in it's registerBeanDefinitions method.

If you like the approach you can find sample implementations on my blogpost.

Reddick answered 6/6, 2015 at 10:50 Comment(1)
you made my day. neat.Trilingual
R
1

I found a similar answer as teabot to create a MockFactory that provides the mocks. I used the following example to create the mock factory (since the link to narkisr are dead): 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>

This also helps to prevent that Spring wants to resolve the injections from the mocked bean.

Rahm answered 29/2, 2012 at 13:9 Comment(0)
P
1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

this ^ works perfectly well if declared first/early in the XML file. Mockito 1.9.0/Spring 3.0.5

Pignut answered 23/12, 2012 at 16:19 Comment(0)
B
1

I developed a solution based on the proposal of Kresimir Nesek. I added a new annotation @EnableMockedBean in order to make the code a bit cleaner and modular.

@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();
    }

}

I have written a post explaining it.

Blaubok answered 6/6, 2015 at 20:0 Comment(0)
E
1

I would suggest to migrate your project to Spring Boot 1.4. After that you can use new annotation @MockBean to fake your com.package.Dao

Exsect answered 5/9, 2016 at 13:10 Comment(0)
C
0

Today I found out that a spring context where I declared a before the Mockito beans, was failing to load. After moving the AFTER the mocks, the app context was loaded successfully. Take care :)

Cyder answered 27/4, 2011 at 19:54 Comment(1)
There is something missing. 8-) You moved what after the mocks?Scamper
S
0

For the record, all my tests correctly work by just making the fixture lazy-initialized, e.g.:

<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>

I suppose the rationale is the one Mattias explains here (at the bottom of the post), that a workaround is changing the order the beans are declared - lazy initialization is "sort of" having the fixture declared at the end.

Stargell answered 4/7, 2013 at 23:6 Comment(0)
S
0

If you're using spring boot 2.2+, you can use @MockInBean as an alternative to @MockBean and keep your Spring context clean:

@SpringBootTest
public class MyServiceTest {

    @MockInBean(MyService.class)
    private ServiceToMock serviceToMock;

    @Autowired
    private MyService myService;

    @Test
    public void test() {
        Mockito.when(serviceToMock.returnSomething()).thenReturn(new Object());
        myService.doSomething();
    }
}

disclaimer: I created this library to avoid Spring Context re-creation caused by @MockBean/@SpringBean that leads to slow build test phases (see Using @MockBean in tests forces reloading of Application Context or the problem with @MockBean)

Shroff answered 17/3, 2021 at 23:47 Comment(0)
S
0

JUnit5 example.

First you have some service that wires in some dependent object.

@Component
public class MyService {
   private final MyRepository repository;

   public MyService(MyRepository repository) {
      this.repository = repository;
   }

   public MyData serviceFindBy(String id) {
      return repository.findById(id).get();
   }
}

Then you have some dependent object, here a Repository:

@Repository
public interface MyRepository extends MongoRepository<MyData, String> {
   Optional<MyData> findById(String id);
}

This is how the JUnit test would look:

import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest(classes = {MyService.class})
@ExtendWith(MockitoExtension.class)
class MyServiceTest {

    @InjectMocks
    MyService myService;

    @MockBean
    MyRepository myRepository;

    @Test
    public void testFindById() {
        when(myRepository.findById(any())).thenReturn(new MyData("123"));
        MyData myData = myService.serviceFindBy("123");
        assertEquals("123", myData.getId());
    }
}
Sulphuric answered 8/1 at 15:35 Comment(0)
C
0

I had a complex EventListeners integration testing scenario, the simplest and fastest approach in my case was to provide a mocked bean or discard it from the context.

I ended up choosing to discard it with the following approach.

I create a jUnit extension class:

public class DisableBeanExtension implements BeforeAllCallback {

    @Override
    public void beforeAll(final ExtensionContext extensionContext) {
        final Class<?> testClass = extensionContext.getRequiredTestClass();
        final DisableBean disableBeanAnnotation = testClass.getAnnotation(DisableBean.class);

        if (disableBeanAnnotation != null) {
            final var applicationContext = (ConfigurableApplicationContext) SpringExtension.getApplicationContext(extensionContext);
            final var registry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
            for (Class<?> classType : disableBeanAnnotation.classes()) {
                registry.removeBeanDefinition(StringUtils.uncapitalize(classType.getSimpleName()));
            }
        }
    }

}

I created a custom annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ExtendWith(DisableBeanExtension.class)
public @interface DisableBean {

    Class<?>[] classes() default {};

}

And finally, I used the custom annotation on my integration test class:

// Other annotations to set up the test class have been omitted
@DisableBean(classes = {BeanToRemoveOne.class, BeanToRemoveTwo.class})
class MyIntegrationTest {

    // Other dependencies and scenarios to set up the test cases have been omitted

}

It's worked fine to me!

Cardiff answered 17/2 at 3:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.