Mikhail Geyer
Mikhail Geyer

Reputation: 1171

@MockBean and @Autowired of the same service in one test class

Is it possible to somehow have in the same test class @MockBean and @Autowired of the same service?

In other words, I would like to have @MockBean service only for one test, while for others tests of the same class I need it as @Autowired.

Upvotes: 14

Views: 13950

Answers (3)

Dmitry Kaltovich
Dmitry Kaltovich

Reputation: 2280

The best solution is to change @MockBean to @SpyBean. And in the method you will be able to do like this:

kotlin

    @SpyBean
    lateinit var serviceMock: Service

    @Test
    fun smallTest()
        `when`(serviceMock.doSomething())
            .thenReturn(false)

        // your test logic
    }

Upvotes: 10

Ahmed Sayed
Ahmed Sayed

Reputation: 1564

This relies on the difference between @MockBean and @Autowired.

@Autowired

only does a lookup in the SpringContext for a bean of that type. This means that you will need to create that bean if you need to 'autowire' it

@MockBean

does exactly what you expect from the name, it creates a 'mock' of the service, and injects it as a bean.

so this

class MyTest {
   @MockBean
   MyService myService;
}

is equivalent to this

@Import(MyTest.Config.class)
class MyTest {

   @Autowired
   MyService myService;

   @TestConfiguration
   static class Config {

      @Bean
      MyService myService() {
         return Mockito.mock(MyService.class);
      }
   }
}

So, if you need to have a different bean of the MyService type in other tests, you need to create the bean in a @TestConfiguration annotated class

@Import(MyTest.Config.class)
class MyTest {

   @Autowired
   MyService myService;

   @TestConfiguration
   static class Config {

      @Bean
      MyService myService() {
         return new MyServiceImpl();
      }
   }
}

Or, in a class annotated with @Configuration

@Import(MyConfig.class)
class MyTest {
   @Autowired
   MyService myService;
}

@Configuration
public class MyConfig {
   @Bean
   MyService myService() {
      return new MyServiceImpl();
   }
}

Upvotes: 6

Yannic Bürgmann
Yannic Bürgmann

Reputation: 6581

I suspect that the source of the evil here is field injection.

Olvier Gierke (now Drotbohm) wrote a blog post about why field injection is evil.

If you can switch to constructor injection you can mock the service just in your test and pass the mock to the class you want to test.

I just want to leave this answer here as a suggestion for others who might have the chance to use constructor injection instead.

Upvotes: 1

Related Questions