#ContextConfiguration not bringing in only the config classes specified - java

I noticed that with #EnableWebMvc I'm getting the following error when running my tests: A ServletContext is required to configure default servlet handling. This issue is temporarily resolved by commenting out #EnableWebMvc then my tests all pass, however I want this in my web app.
I read in this post that I could put the #EnableWebMvc in another config class that isn't included in the tests(?). So I've tried this:
AppConfig.java
#Configuration
#ComponentScan(basePackages = "biz.martyn.budget")
#PropertySource("classpath:prod.properties")
#EnableTransactionManagement
public class AppConfig {
#Autowired
private Environment env;
#Bean(name = "dataSource", destroyMethod = "shutdown")
#Profile("prod")
public DataSource dataSourceForProd() {...
WebMvcConfig.java
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {...
Then in my tests I'm attempting:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#Transactional
public class FundRepositoryTest {...
However, I'm still seeing the same error in my tests. I know it's the #EnableWebMvc as they all pass when I remove this. Have I misunderstood something with how #ContextConfiguration annotation works? By the way, I'm using Spring version 4.2.2.RELEASE for all my spring-* dependencies if that helps.
Below is also the error I'm seeing in my test run:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)

I'm still not sure why the #ContextConfiguration annotation isn't only accepting the class(es) I provide but I have found that #WebAppConfiguration added to each test class provides the context required:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#Transactional
#WebAppConfiguration // <-- added this
public class FundRepositoryTest {...
It's an additional annotation I need to add though but my tests run now.

Related

How to exclude #EnableJpaRepositories from test?

I have a main #SpringBootApplication which needs to scan a specific package in order to enable the JPA repositories, so I use #EnableJpaRepositories to specify that. Right now I'm implementing unit tests and I want to test the Controller component only, so I followed the tutorial in the official docs where they use #WebMvcTest(MyController.class) to test a controller with a service dependency.
The problem is that this is not working for me because it is trying to load the JpaRepositories that I specify in the main Spring Boot application (when I comment the #EnableJpaRepositories in the main class the test runs without problem).
I'm guessing I need to create a specific configuration for the test class so it can ignore the main configuration (since I only want to load the Controller and mock the service layer), but I don't know how to create such. I tried adding an empty configuration, but it is still failing with the same error:
#TestConfiguration
static class TestConfig {}
This is the error I get:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'failureTaskHandler': Unsatisfied dependency expressed through field 'myManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'msgManager': Unsatisfied dependency expressed through field 'inboundManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inboundManager': Unsatisfied dependency expressed through field 'messageRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageRepository' defined in com.example.MessageRepository defined in #EnableJpaRepositories declared on MonitorApplication: Cannot create inner bean '(inner bean)#45e639ee' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#45e639ee': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
And my test class:
#WebMvcTest(MyController.class)
public class MyControllerTest {
#Autowired private MockMvc mvc;
#MockBean private MyService service;
// Tests here
// #Test
// public void...
}
MyController class:
#RestController
#CrossOrigin
#RequestMapping("/api")
#Slf4j
public class MyController {
#Autowired private MyService service;
#PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody SearchResponse getOrders(#RequestBody SearchRequest orderSearchRequest) {
log.info("Receiving orders request...");
return service.getOrders(orderSearchRequest);
}
}
Quick solution
Remove #EnableJpaRepositories from your Spring Boot Application class. Use:
#SpringBootApplication
public class MainApplication {
}
in place of
#SpringBootApplication
#EnableJpaRepositories
public class MainApplication {
}
In this case Spring Boot will find Spring Data JPA on the classpath and uses auto-configuration to scan packages for the repositories.
Use #EnableJpaRepositories to scan a specific package
Use #NikolaiShevchenko solution (it is incorrect) with a separate configuration, but without explicit importing it, by #Import({ DataConfiguration.class }), (because tests will be explicitly import the configuration too) and let Spring Boot find your configuration during packages scan.
#SpringBootApplication
public class MainApplication {
}
#Configuration
#EnableJpaRepositories(basePackages = "com.app.entities")
public class JpaConfig {
}
Important
Don't forget to add basePackages property, if you put your configuration in a separate package.
Declare separate configuration
#Configuration
#EnableJpaRepositories
public class DataConfiguration { ... }
Import it into the application
#SpringBootApplication
#Import({ DataConfiguration.class })
public class MainApplication { ... }
but don't import into MyControllerTest

Spring Framework Error creating bean with name and No qualifying bean of type

I'm starting on Spring Framework and have some problems: After I changed the packages structure for my #Service, #Controller, #Entity and #Repository classes my project stopped working. Before the change my project was running correctly, and now I do not know why it wont start up.
The package structure was something like:
com.sandbox.config
com.sandbox.service
com.sandbox.service.impl
com.sandbox.controllers
com.sandbox.dao
com.sandbox.dao.impl
com.sandbox.entities
And I changed to:
com.sandbox.config
com.sandbox.business.service
com.sandbox.business.service.impl
com.sandbox.frontend.controllers
com.sandbox.persistency.dao
com.sandbox.persistency.dao.impl
com.sandbox.persistency.entities
My configuration classes are:
SpringJdbcConfig:
package com.sandbox.config;
#Configuration
#ComponentScan(basePackages = { "com.sandbox.*" })
public class SpringJdbcConfig {...}
WebConfig:
package com.sandbox.config;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.sandbox.*" })
public class WebConfig implements WebMvcConfigurer {...}
Does anybody know what I am doing wrong?
I am getting these errors at the eclipse output console:
Error creating bean with name 'staffServiceImpl': Unsatisfied dependency expressed through field 'staffDao'.
No qualifying bean of type 'com.sandbox.persistency.dao.StaffDao' available: expected at least 1 bean which qualifies as autowire candidate.
And I receive the same errors for each class of my project.
Change ComponentScan as below and try again:
#ComponentScan({ "com.sandbox.business.service","com.sandbox.persistency.dao" })
after hours searching and testing I found the solution to my problem. If I change the package structure adding a new package level, Spring duplicates Bean classes if #ComponentScan is used more than one time. That doesn't happen if You keep all packages at the same level. In my case I was using #ComponentScan in classes SpringJdbcConfig and WebConfig.
I had it set up like this:
SpringJdbcConfig:
#ComponentScan(basePackages = { "com.sandbox.*" })
public class SpringJdbcConfig {...}
WebConfig:
#ComponentScan(basePackages = { "com.sandbox.*" })
public class WebConfig implements WebMvcConfigurer {...}
And I changed it to:
SpringJdbcConfig:
#ComponentScan({ "com.sandbox.config", "com.sandbox.persistency.*" })
public class SpringJdbcConfig {...}
WebConfig:
#ComponentScan({ "com.sandbox.config", "com.sandbox.business.*", "com.sandbox.persistency.*", "com.sandbox.frontend.*" })
public class WebConfig implements WebMvcConfigurer {...}
I hope it helps. :)

ComponentScan code smell? When running unit tests, I get Error creating bean, no qualifying bean of type, even though bean exists in Spring beans list

i have a config class in a config package that looks like this:
package com.x.y.appName.config
#ComponentScan(basePackageClasses = { Application.class })
#Configuration
public class AppConfig {
my project is failing on build within SomeClass that uses the AppConfig bean, saying:
Error creating bean with name 'someClass': Unsatisfied dependency
expressed through field 'appConfig'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
But when I stdout print the list of beans Spring is aware of, it lists appConfig there
SomeClass is also in the config package, and looks like this:
package com.x.y.appName.config;
#Configuration
public class SomeClass implements WebMvcConfigurer {
#Autowired
AppConfig appConfig;
but if i add this to SomeClass, it builds fine and all tests pass:
#ComponentScan("com.x.y.appName.config")
in the past ive never needed to ComponentScan the same package that another bean is also declared in
again to clarify, i can bootRun the app just fine, but this spring error is throwing during build or test. do i need to add something to the unit tests? I dont have unit tests for either of the above classes as they would be too frivolous. So what could be going on? Do I need to annotate other unit tests somewhere?

How to autowired profile configuration class on startup?

I'd like to create different database profile classes, each for a purpose of development, production and testing.
I tried the following with the help of http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/, but it won't wire correctly. Why?
interface DataConfig {
DataSource dataSource();
}
#Configuration
#Profile("dev")
public class StandaloneDataConfig implements DataConfig {
#Bean
#Override
public dataSource dataSource() {
//return the ds
}
}
#Configuration
#Profile("prod")
public class JndiDataConfig implements DataConfig { ... }
#Configuration
#PropertySource({"classpath:config.properties"})
class AppConfig {
#Autowired
private DataConfig cfg;
}
#Configuration
#ComponentScan
#Import(AppConfig.class)
#EnableTransactionManagement
public class SpringBootConfig extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
config.properties:
spring.profiles.active=dev
Result: Exception on startup
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private DataConfig dataConfig; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [DataConfig] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 34 common frames omitted
Spring 4.0.3.RELEASE
My setup though seem to work in general: if I remove the #Profile annotation on one of the databases, everything wires up correctly.
You need to rename your config.properties file to application.properties for Spring Boot to pick it up automatically.
I haven't tested your configuration, but my best guess (following some investigation I've done for this post) is that #PropertySources are not available for a #Conditional annotated #Configuration class when autowiring occurs.
According to the source code, #Profile is a #Conditional flavor of annotation with a Condition implementation.
For #PropertySource to be available when they are needed I think you would need your own custom #Conditional implementation just like in the SO post I mentioned above, where you define not a Condition but a ConfigurationCondition making sure to be used at ConfigurationPhase.REGISTER_BEAN phase.

Why unit testing with Spring 3.1 WebMvcConfig fails?

From Spring 3.1, we can use JavaConfig more easily thanks to #Enable* annotations.
So I made a WebConfig to set WebMvc configuration, and tried to test it. But if I extends WebMvcConfigurerAdapter or WebMvcConfigurationSupport with WebConfig the unit test fails because of lack of ServletContext. The code and messages look like below.
WebConfig.java
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {}
Test.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=WebConfig.class)
public class TestFail {
#Test
public void test() {}
}
Message
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
...
Caused by: java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer.<init>(DefaultServletHandlerConfigurer.java:54)
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping(WebMvcConfigurationSupport.java:253)
at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1.CGLIB$defaultServletHandlerMapping$10(<generated>)
at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1$$FastClassByCGLIB$$19b86ad0.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:215)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:280)
at com.zum.news.comments.web.WebConfig$$EnhancerByCGLIB$$8bbfcca1.defaultServletHandlerMapping(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:149)
... 41 more
How to unit test the WebConfig properly?
Edit
As Garcia said, this bug is fixed in Spring 3.2.0.RC1.
Just add #WebAppConfiguration annotation in the test class.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes=WebConfig.class)
public class TestFail {
#Test
public void test() {}
}
As Guido mentioned previously, this has been solved as of 3.2. The following are the details of how to take advantage of the new test improvements. To ensure that a servlet context is loaded for your test, you need to annotate your test with #WebAppConfiguration and define AnnotationConfigWebContextLoader as your context loader, as below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(
classes = MyWebConfig.class,
loader = AnnotationConfigWebContextLoader.class)
public class MyTest {
//...
}
If #EnableWebMvc annotation require ServletContext then I suggest to split your config to beans definitions which will be used in unit tests and other configuration which used by application and framework. In this case application will import both configs and unit tests will import only one.
BeansConfig.java:
#Configuration
public class BeansConfig {
#Bean
MyBean myBean() {
return new MyBean()
}
}
WebConfig.java:
#Configuration
#EnableWebMvc
#Import(BeansConfig.class)
public class WebConfig extends WebMvcConfigurationSupport {}
TestFail.java:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=BeansConfig.class)
public class TestFail {
#Test
public void test() {}
}
There is a bug in Spring 3.1, you can find the answer in these two issues:
https://jira.springsource.org/browse/SPR-5243
https://jira.springsource.org/browse/SPR-9799
Please let us know if you find a workaround for Spring 3.1, if not we must wait until 3.2 is out there. I have to say that I've just tried it with Spring 3.2.0.M2 and it is still not working for me.
Another recommendation that I have would be to use spring-test-mvc, which internally creates a mock servlet context for the Controller tests to work.
If you want to continue with your approach, you may then have to create your own Spring Context loader that additionally initializes a Mock servlet context - along these lines:
http://tedyoung.me/2011/02/14/spring-mvc-integration-testing-controllers/,
From Spring-test-mvc source

Categories

Resources