Reputation: 2251
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?
Upvotes: 15
Views: 18848
Reputation: 32026
For Spring Boot, @MockBean
(and @SpyBean
) are likely your best bets as noted in Grzegorz Poznachowski's answer.
For non Spring Boot Applications, an alternative is to use @Primary
to annotate the mock/stub @Bean
/@Service
definition. That annotation "indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency." As such, your mock will be used over the "real" one when wiring other Beans/Configurations.
@SpringJUnitWebConfig(MyAppConfig.class)
public class MyUintTest
{
@Autowired
private ServiceInterface serviceMock;
@Autowired
private SomeClass someClassMock;
@Test
public void myTest()
{
when(someClassMock.getProperty()).thenReturn(properties.get());
// . . .
}
@Service
@Primary
static class TestService implements ServiceInterface
{
// . . .
}
@Configuration
static class Config
{
@Bean
@Primary
public SomeClass someClassBean()
{
return Mockito.mock(SomeClass.class);
}
}
}
Upvotes: 0
Reputation: 644
Spring Boot has @MockBean
and @SpyBean
annotations exactly for this purpose:
Declaration is simple:
@MockBean
private MyService myService;
Spring Boot will inject Mockito mock there instead of the actual bean.
Upvotes: 8
Reputation: 1212
You can name your beans, in your case something like
@Service("testService")
public class TestMyService implements IMyService {
}
and in your test class, you can explicitely ask for test service using @Qualifier
, like
@Qualifier("testService")
@Autowired
private IMyService myService;
Upvotes: 0
Reputation: 868
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;
}
}
}
Upvotes: 9