In my spring boot application I have a service bean (with @Service annotation) and I want to mock this service in particular JUnit test (not in all tests). How can I replace this service bean for just one particular test?
Service declared as:
@Service
public class LocalizationServiceImpl implements LocalizationService {
...
}
App configuration class:
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages="my.package")
@EntityScan
public class Application {
...
}
Test class:
@Transactional
@SpringApplicationConfiguration(classes = LocalizationServiceTest.LocalizationServiceTestConfig.class)
public class LocalizationServiceTest extends ESContextTest {
@Autowired
private LocalizationService locService;
@Configuration
public static class LocalizationServiceTestConfig {
@Bean
public LocalizationService localizationServiceImpl() {
return mock(LocalizationService.class);
}
}
}
And test class parent:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public abstract class ESContextTest {
...
}
But this does not work. When I run the test then original LocalizationServiceImpl is used for autowired locService property. This appeared in log file: Skipping bean definition for [BeanMethod:name=localizationService,declaringClass=...LocalizationServiceTest$LocalizationServiceTestConfig]: a definition for bean 'localizationService' already exists. This top-level bean definition is considered as an override.
When I use different name for @Bean method in LocalizationServiceTestConfig e.g. localizationServiceMock() (to be different from original implementation classname) then spring throws
'No qualifying bean of type is defined: expected single matching bean but found 2: localizationServiceImpl,localizationServiceMock'
So I thought it is correct to use the same name.
Only working solution is to remove @Service annotation from LocalizationServiceImpl class and create configuration for normal (not test) app run like
@Configuration
public class BeansConfig {
@Bean
public LocalizationService localizationServiceImpl() {
return new LocalizationServiceImpl();
}
}
Then in test run the correct mock implementation is autowired.
But it should be possible to do the same via @Service annotation, shouldn't?
Thanks for advice.
In Spring Boot, we can create a @TestConfigurationclass to initialize some beans for testing class only. P.S Tested with Spring Boot 2 1. @TestConfiguration + @Import This @TestConfigurationclass will not pick up by the component scanning, we need to import it manually.
In Spring Boot, @TestConfiguration annotation can be used to define/override beans for unit tests. @TestConfiguration classes (in test folder) can only be used by selective test classes which explicitly want to import them via @import annotation. @Configuration classes (in test folder), are available for all tests without explicitly importing them.
The given answer seems useful but just in case it can help someone, testing service is not so obvious in Spring (IMHO). The SpyBean annotation turns out to be really handy:
@Service on Concrete Classes Contrary to what we've seen above, it's quite a common practice to annotate the implementation classes instead of abstract classes or interfaces. In this way, our goal is mostly to tell Spring this class is going to be a @Component and mark it with a special stereotype, which is @Service in our case.
Found a solution by myself: rename localizationServiceImpl()
e.g. to localizationServiceMock()
in class LocalizationServiceTestConfig
and annotate the method with @Primary
.
But why simply redefining bean with the same 'id' doesn't work like via xml-config?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With