Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace a bean with a testing mock in Spring Boot for integration testing

I have a spring app and integration tests for this app. I would like to replace a bean with a mock bean.

My real bean looks like this

@Service
public class MyService {

}

and for testing I would like it to be replaced

@Service
public class TestMyService {

}

All I can think of is to use profiles for different services. For example:

@Service
@Profile("!test")
public class MyService implements IMyService {

}

@Service
@Profile("test")
public class TestMyService implements IMyService {

}

And then I autowire the bean like this

@Autowired
private IMyService myService;

Is there a better way?

like image 511
Sergei Ledvanov Avatar asked Jan 05 '19 11:01

Sergei Ledvanov


People also ask

Does integration testing use mocks?

In an integration test, there is no need to mock away parts of the application. You can replace external systems, but the application works in an integrated way.

How do you mock in spring boot test?

This is easily done by using Spring Boot's @MockBean annotation. The Spring Boot test support will then automatically create a Mockito mock of type SendMoneyUseCase and add it to the application context so that our controller can use it.


2 Answers

Spring Boot has @MockBean and @SpyBean annotations exactly for this purpose:

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans

Declaration is simple:

@MockBean
private MyService myService;

Spring Boot will inject Mockito mock there instead of the actual bean.

like image 114
Grzegorz Poznachowski Avatar answered Oct 26 '22 07:10

Grzegorz Poznachowski


My personal preference is to avoid loading the compete context for testing. Therefore I like my test to focus on a subset of beans. It usually means I outline beans which I use in tests:

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = {TestMyService.class, OtherClassNeededForTesting.class}
)
public class DelaysProviderTest {

}

If more configuration is needed I tend to prepare a separate configuration class for tests:

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = MyTest.Cfg.class
)
public class MyTest {

    @Import({
        // .. some classes to import including TestMyService.class
    })
    @Configuration
    public static class Cfg {

    }

}

When even more configuration is needed (or mocking), I use the test configuration for providing appropriate mocks

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = MyTest.Cfg.class
)
public class MyTest {

    @Import({
        // .. some classes to import
    })
    @Configuration
    public static class Cfg {

        @Bean
        public IMyService service() {
            IMyService mock = Mockito.mock(IMyService.class);
            when(mock.someMethod()).thenReturn("some data");

            return mock;
        }

    }

}
like image 28
Jakub Marchwicki Avatar answered Oct 26 '22 08:10

Jakub Marchwicki