Adding configuration class to SpringBootTest breaks component scan - spring-boot

I am trying to disable real Mongo connection and replace it with Fongo mock in tests.
Here is my test class:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ControllerTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private MyService service;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void performTest() throws Exception {
... logic ...
}
}
It works fine unless I try to add my configuration file changing this line:
#SpringBootTest
to this:
#SpringBootTest(classes = TestConfig.class)
config class itself:
#Configuration
#ComponentScan
#EnableMongoRepositories
public class TestConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "FongoDB";
}
#Override
public Mongo mongo() {
return new Fongo(getDatabaseName()).getMongo();
}
}
Then application fails to find beans and throws the next exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 28 more
How can I fix it and apply additional configuration properly?

try use
#SpringBootTest
#Import(value = TestConfig.class)
instead of
#SpringBootTest(classes = TestConfig.class)

keep #SpringBootTest and then create a class using #TestConfiguration with the beans in as follows:
#TestConfiguration
public class TransactionManagerTestConfiguration {
#Bean
public String getDatabaseName() {
return "FongoDB";
}
#Bean
public Mongo mongo() {
return new Fongo(getDatabaseName()).getMongo();
}
}
As per the javadoc: Configuration that can be used to define additional beans or customizations for a test. Unlike regular Configuration classes the use of TestConfiguration does not prevent auto-detection of SpringBootConfiguration.

Related

Upgrade Spring Boot 2.0.8 to 2.1.2 - Failed to load ApplicationContext in tests

When upgrading spring from 2.0.8 to 2.1.2 (using JDK 8) the application starts and runs fine but tests fail due to java.lang.IllegalStateException: Failed to load ApplicationContext.
I am using an abstract class which some tests do extend.
#SpringBootTest
#RunWith(SpringRunner.class)
public abstract class AbstractTestkonfiguration {
#TestConfiguration
static class TestEnvironmentConfiguration {
#Component
#PropertySource(value = "classpath:my-test.properties")
#ConfigurationProperties(prefix = "my")
public static class MyTestProperties extends EnvironmentalProperties {
}
}
}
The class EnvironmentalProperties is a class for type-safe configuration properties (Doc)
Before the upgrade that worked and a class of EnvironmentalProperties was provided but now I am getting a
[...]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.abc.EnvironmentalProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#javax.inject.Inject()}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1651)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1210)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1164)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 90 more
Could it be related to a change in Nested Configuration Class Detection (Upgrading to Spring Framework 5.x)? If so, how can I configure a bean of EnvironmentalProperties only for tests?
Update: Even if used as follows it doesn't work (same result).
#SpringBootTest
#RunWith(SpringRunner.class)
public abstract class AbstractTestkonfiguration {
#Configuration
public static class TestEnvironmentConfiguration {
#Bean
public MyTestProperties environmentalProperties(){
return new EnvironmentalProperties() {
// manual creation of an instance
}
}
}
}
There are a few changes that you have to make.
You haven't enabled the configuration properties via #EnableConfigurationProperties
The property source needs to be injected at test class
Remove #Component annotation
Here is a working example;
src/test/resources/my-test.properties
my.server.name=foo
my.server=test
And
src/main/resources/application.properties
my.name=production
The production configuration.
#ConfigurationProperties(prefix = "my")
#PropertySource(value = "classpath:application.properties")
public class EnvironmentalProperties {
private String name;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
#SpringBootTest
#TestPropertySource(value = {"classpath:my-test.properties", "classpath:application.properties"})
#RunWith(SpringRunner.class)
public class AbstractTestkonfiguration {
#Autowired
private MyTestProperties myTestProperties;
#TestConfiguration
#EnableConfigurationProperties(MyTestProperties.class)
public static class TestEnvironmentConfiguration {
#ConfigurationProperties(prefix = "my")
public static class MyTestProperties extends EnvironmentalProperties {
private String server;
public String getServer() {
return server;
}
public void setServer(final String server) {
this.server = server;
}
}
}
#Test
public void check_configuration () {
Assert.assertEquals(myTestProperties.getServer(), "test");
Assert.assertEquals(myTestProperties.getName(), "production");
}
This works on Java 11 & spring-boot 2.1.2.RELEASE. Please note, this is only an example. You will have to adapt it to your project properly.

Spring: Register a component within a test class

I am registering an ErrorHandler for my Spring Scheduler and would like to test that is is correctly registered in a SpringTest
So far I have tried:
Handler
#Component
public class ScheduledErrorHandler implements ErrorHandler {
#Autowired
private ErrorService errorService;
#Override
public void handleError(final Throwable t) {
errorService.handle(t);
}
}
Registering the Handler
#EnableScheduling
#Configuration
public class SchedulingConfiguration implements SchedulingConfigurer {
#Autowired
private ScheduledErrorHandler handler;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setErrorHandler(handler);
scheduler.initialize();
taskRegistrar.setScheduler(scheduler);
}
//...
}
Testing it's registered
#ContextConfiguration(classes = {
SchedulerConfiguration.class,
SchedulerErrorHandler.class
})
#RunWith(SpringRunner.class)
public class SchedulerErrorHandlerTest {
#MockBean
private ErrorService service;
#Autowired
private ExampleScheduledJob job;
#Test
public void verifyHandlerGetsCalled() {
// Wait until the job runs
if(!job.latch.await(5, SECONDS)) {
fail("Job never ran");
}
verify(service).handle(any(RuntimeException.class));
}
#Component
public static class ExampleScheduledJob {
private final CountDownLatch latch = new CountDownLatch(1);
#Scheduled(fixedRate=1000)
public void run() {
latch.countDown();
throw new RuntimeException("error");
}
}
}
However when I do this I get a DependencyNotFound error saying Spring cannot create my test class as no Bean named ExampleScheduledJob can be found. How can I register it only for the sake of this test?
Error creating bean with name
'com.example.demo.SchedulerErrorHandlerTest': Unsatisfied dependency
expressed through field 'job'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.example.demo.SchedulerErrorHandlerTest$ExampleScheduledJob'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
This should work
#ContextConfiguration(classes = {
SchedulingConfiguration.class,
SchedulerErrorHandlerTest.ExampleScheduledJob.class,
ScheduledErrorHandler.class
})
#RunWith(SpringRunner.class)
You can register your test configuration class (ExampleScheduledJob) as indicated above. Since it is a static inner class, you need to use it like SchedulerErrorHandlerTest.ExampleScheduledJob

springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type

I'm trying to mock rest api call but facing an error while testing the controller class using WebMvcTest,
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.abc.center.entities.repositories.SomeRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 41 more
I have an project structure something like this,
I tried many ways but no luck, Below is my restcontroller and its Mockito test class and repository,
#Slf4j
#Component
#RequestMapping()
#Setter
public class SomeController {
// Variable initialization
#Autowired
private SometRepository someRepository;
public void sendData(RecordNo rocordNo, String xmlString, SomeFile file) throws ValidationException{
ClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
header.add("x-api-key",api_key);
header.add("Content-Type",content_type);
header.add("Cache-Control",cache_control);
HttpEntity<String> request = new HttpEntity<>(xmlString, header);
try {
restTemplate.postForEntity(getUri(rocordNo,file), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException(e.getResponseBodyAsString());
}
}
getUri(RecordNo rocordNo SomeFile file){
// here I need someRepository which is an interface
}
}
public interface TestRepository extends PagingAndSortingRepository<RecordNo, Integer> {
//queries to repositories
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(SomeController.class)
public class SomeControllerTestClass {
private TestController serviceToTest;
private String xmlString = "String";
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Mock
private TestRepository testRepository;
#Before
public void init(){
serviceToTest.setTestRepository(testRepository);
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
MockitoAnnotations.initMocks(this);
}
#Test
public void verifySafe2Call() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.post("someuri")
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_JSON)
.content(xmlString)).andExpect(jsonPath("$.responseMessage").value("Validation succeeded"))
.andExpect(jsonPath("$.responseCode").value("SUCCESS"))
.andDo(print());
}
Does my project structure is making any problem? I know it is not able to find the definition but not getting why it is so.
Any suggestion would be nice.
Try this,
add following code to the controller:
#Autowired
private SometRepository someRepository;
public void setRepository(SomeRepository repository) {
this.repository = repository;}
I don't see #EnableJpaRepositories annotation.
Your config should be annotated with this annotation and Spring will scan the needed packages for repositories.
#Configuration
#EnableJpaRepositories(basePackages = {"com.abc.center.entities.repositories"})
public class JpaConfig {
}
I had a similar problem writing a unit/integration test with #WebMvcTest and I solved it by adding #ComponentScan("com.abc.center.entities.repositories") to SomeController (in your case).

Unsatisfied dependency during test

I have a spring boot 2.0.0 M2 application who run well.
I use autowired on constructor
#RequestMapping(value = "/rest")
#RestController
public class AddressRestController extends BaseController{
private final AddressService AddressService;
#Autowired
public AddressRestController(final AddressService AddressService) {
this.AddressService = AddressService;
}
...
}
#Service
public class AddressServiceImpl extends BaseService implements AddressService {
#Autowired
public AddressServiceImpl(final AddressRepository AddressRepository) {
this.AddressRepository = AddressRepository;
}
private final AddressRepository AddressRepository;
...
}
public interface AddressRepository extends JpaRepository<Address, Integer>, AddressRepositoryCustom {
}
#Repository
public class AddressRepositoryImpl extends SimpleJpaRepository implements AddressRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Autowired
public AddressRepositoryImpl(EntityManager em) {
super(Address.class, em);
}
...
}
When i try to run a basic test
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
#Autowired
private AddressService service;
#MockBean
private AddressRepository restTemplate;
#Test
public void getAddress(){
MockitoAnnotations.initMocks(this);
Pageable page = PageRequest.of(0, 20);
Page<Address> pageAdr = mock(Page.class);
given(this.restTemplate.findAll(page)).willReturn(pageAdr);
Page<AddressDto> pageDto = service.getAddress(page);
}
}
I get this error
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'com.sonos.arcor.service.AddressServiceTest': Unsatisfied dependency
expressed through field 'service'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type ''com.sonos.arcor.service.AddressService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I don't understand why i get this error.
You need to annotate the test with SpringBootTest so that spring initialize an application context
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
// the remaining test
}
Also you do not need MockitoAnnotations.initMocks(this);
Spring takes care of the mock handling
When [#MockBean is]used on a field, the instance of the created mock will also be
injected. Mock beans are automatically reset after each test method
see Mocking and spying beans

Spring MockBean annotation cause " No qualifying bean of type MockitoPostProcessor is defined"

I find if "old style", like RunWith annotation and SpringApplicationConfiguration annotation, mixed with "new style" MockBean annotation, it looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class SomeUnitTest {
#InjectMocks
#Autowired
private IWantTestThisService iWantTestThisService;
#MockBean
private DependencyInAboveService dependencyInAboveService;
#Test
public void testSomething() {
// your test
}
}
It will case exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.test.mock.mockito.MockitoPostProcessor] is defined
To fix this exception, you should upgrade your code to
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class SomeUnitTest {
#Autowired
private IWantTestThisService iWantTestThisService;
#MockBean
private DependencyInAboveService dependencyInAboveService;
#Test
public void testSomething() {
// your test
}
}

Resources