java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling - spring

I have the following test class:
#ActiveProfiles({ "DataTC", "test" })
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {BaseTestConfiguration.class, DataTestConfiguration.class, JpaConfiguration.class, PropertyPlaceholderConfiguration.class })
public class RegularDayToTimeSlotsTest {
...
The issue seems to come from the BaseTestConfiguration class:
#Configuration
#ComponentScan(basePackages = { "com.bignibou" }, excludeFilters = { #Filter(type = FilterType.CUSTOM, value = RooRegexFilter.class),
#Filter(type = FilterType.ANNOTATION, value = Controller.class), #Filter(type = FilterType.ANNOTATION, value = ControllerAdvice.class) })
public class BaseTestConfiguration {
}
I systematically get this exception:
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:329)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$bb4ceb44.CGLIB$defaultServletHandlerMapping$22(<generated>)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$bb4ceb44$$FastClassByCGLIB$$368bb5c1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:326)
at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerByCGLIB$$bb4ceb44.defaultServletHandlerMapping(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
... 43 more
I am not sure how to get around this issue. Somehow Spring is looking for a ServletContext when I run the test and I get the above exception...

One of your #Configuration classes is obviously annotated with #EnableWebMvc. That's how DelegatingWebMvcConfiguration ends up in your stack trace, since it is imported by #EnableWebMvc.
So although you think you don't need a WebApplicationContext (and hence a ServletContext), you in fact do need it simply because you are loading an application context with #EnableWebMvc.
You have two options:
Compose the configuration classes for your integration test so that you are not including the web-related configuration (i.e., the #Configuration class(es) annotated with #EnableWebMvc).
Annotate your test class with #WebAppConfiguration as suggested in other comments above.
Regards,
Sam (author of the Spring TestContext Framework)

It seems like you are missing
#WebAppConfiguration
from your test class.
The documentation states
The resource base path is used behind the scenes to create a
MockServletContext which serves as the ServletContext for the test’s
WebApplicationContext.
Typically a Servlet container would provide the ServletContext. Since you are in a testing environment, you need a fake. #WebAppConfiguration provides that.

For you to instantiate the Servlet context, you would have to use the annotation.
#WebAppConfiguration
A class-level annotation that is used to declare that the ApplicationContext loaded for an integration test should be a WebApplicationContext. The mere presence of #WebAppConfiguration on a test class ensures that a WebApplicationContext will be loaded for the test, using the default value of "file:src/main/webapp" for the path to the root of the web application (i.e., the resource base path). The resource base path is used behind the scenes to create a MockServletContext which serves as the ServletContext for the test’s WebApplicationContext.

I was getting a similar error but whilst running the application normally rather than trying to run tests.
It turns out if you're making use of a custom PermissionEvaluator then you need to declare it in a separate #Configuration class to the one with your main Spring security configuration in.
See: How do I add method based security to a Spring Boot project?
There is also an open Github issue: https://github.com/spring-projects/spring-boot/issues/4875

Related

how to override Spring Boot autowired component during testing

I'm trying to write tests for a Spring Boot batch application.
I have an interface "WsaaClient" and two implementations, I need to use one of them for normal execution and the other for testing purposes.
In the project, I have FCEClient class that has an autowired field "LoginManager", which has an autowired field "WsaaClient".
#Component
#Profile("!dev")
public class FCEClient implements IFCEClient {
#Autowired
LoginManager loginManager;
#Component
public class LoginManager {
#Autowired
WsaaClient client;
#Component
public class AfipWsaaClientSpring extends AfipWsaaClient {
AfipWsaaClient is in a non-spring maven dependency. It implements WsaaClient.
Running the Spring Batch application works well and AfipWsaaClientSpring is picked.
Now I want to write a test and need to use a dummy implementation for WsaaClient.
So I put under src/test/java this class:
#Component
public class TestWsaaClientSpring implements WsaaClient {
And this test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration
public class FceBatchApplicationTests {
private JobLauncherTestUtils jobLauncherTestUtils;
#Test
public void testJob() throws Exception {
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
}
}
Running it from JUnit Launcher on Eclipse throws:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'afipWsaaClientSpring' defined in file [/home/guish/vmshare/eclipsews/ec/ec-batch/target/classes/com/mycompany/AfipWsaaClientSpring.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.mycompany.AfipWsaaClientSpring]: Constructor threw exception; nested exception is java.io.FileNotFoundException: ./wsaa_client.properties (No such file or directory)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1303) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1197) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
The FileNotFoundException is not relevant, the file is not present because of running as a test and Spring Boot should not pick the AfipWsaaClientSpring implementation.
How can I override the Autowired option in my test code and choose TestWsaaClientSpring instead?
And just in case, how can I prevent Spring Boot from instantiating the AfipWsaaClientSpring when running as a test?
Annotation #SpringBootTest has 'properties' attribute (https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html).
so, you can specify spring profile like this,
#SpringBootTest(properties = {"spring.profiles.active=test"}, classes=MyConfiguration.class)
As mentioned by Charles Lee you could provide the active profile for SpringBootTest. Also you could do this with the annotation #ActiveProfile("theprofile") on your FceBatchApplicationTests class.

DI in tests without using spring boot (#SpringBootTest)

Switching from spring boot back to "normal" spring because the app only uses some jdbc code to "upsert" into a postgresql database.
1)
tried annotating the test class with:
#RunWith(SpringJUnit4ClassRunner.class)
public class DBIntegration {
results in:
java.lang.IllegalStateException: Failed to load ApplicationContext
2)
tried annotating the class with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
public class DBIntegration {
[main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [de.mydomain.myproject.DBIntegration]: no resource found for suffixes {-context.xml}.
No exceptions, but java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=insertDataFrom_sometest],
3) tried annotating the class with:
#Component
public class DBIntegration {
dependency injection does not work in this case, the expected service
(to be injected) throws a nullpointerexception

Can Not Get #Component As A Bean In Unit Test For Spring Boot Application with Apache Camel

I am doing unit test for my Spring Boot application with Camel. When the application runs, it can get bean which is configured as a #Component
#Component("agencyExporterProcessor")
public class AgencyExporterProcessor {}
and I get the bean like this :
from(getTriggerExportEndpoint())
.routeId(getTriggerExportId())
// When shutting down, Camel will wait until the batch completed
.shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
.log("[SamCustomExporter] - RouteId:${routeId} - Begin at ${date:now:MM/dd/yyyy HH:mm:ss.SSS}")
.setHeader(Messaging.Names.SAM_DATA_AGENCY_CONFIGURATION_HEADER_KEY.toString(), constant(getConfiguration()))
// Initialize a list to store exported CSV file names
.bean(agencyExporterProcessor, "prepareExportedFileList")
But when I test, the route cannot get the bean"
org.apache.camel.FailedToCreateRouteException: Failed to create route agencyExporterRoute_triggerExport at: >>> Bean[ref:agencyExporterProcessor method:prepareExportedFileList] <<< in route: Route(agencyExporterRoute_triggerExport)[[From[direct:agency... because of No bean could be found in the registry for: agencyExporterProcessor
This is how I configured my unit test class:
#DirtiesContext
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#SpringBootApplication
#SpringBootTest(classes = SamCustomExporterSpringApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class AgencyExporterRouteTest extends BaseRouteTestSupport {}
Please give advice!!!! Many thanks
You should refer to the bean name as a String value in the Camel route:
.bean("agencyExporterProcessor", "prepareExportedFileList")

Spring Boot ConfigurationProperties fail to initialize for integration testing

Using gradle (3.4.1) with an integrationTest configuration, the tests using the ConfigurationProperties of Spring Boot (1.5.1.RELEASE) is failing to initialize even though the application initializes correctly (./gradlew bootRun). The class annotated with ConfigurationProperties is similar to the following
#Component
#ConfigurationProperties(prefix = "foo")
#Validated
public class AppConfiguration {
#NonNull
private URL serviceUrl;
...
The configuration file does have getters and setters. The error that is generated is similar to the following
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'AppConfiguration': Could not bind properties to AppConfiguration
....
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult
Field error in object 'foo' on field 'serviceUrl': rejected value [null]; codes ...
The configuration class of the integration test is annotated as follows
#Configuration
#ComponentScan(...)
#EnableConfigurationProperties
#EnableIntegration
public static class ContextConfiguration {}
The test class had the following annotations
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ReleaseTest {
...
After looking at the Spring Boot code for the ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization() it suggested that the property source was not being discovered. Adding the org.springframework.boot:spring-boot-starter-test artifact as a compile-time dependency and modifying the context configuration of the test class to
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
the AppConfiguration class was initialized properly using a YAML-based properties file.
An alternative is to add
#TestPropertySource("classpath:/application.properties")
This approach doesn't require the spring-boot-starter-test dependency and requires that a "traditional" properties file is used (a YAML file will not work with this approach).

Play Framework with Spring, #Transactional not working

I'm using Play Framework (2.2.2) in combination with Spring (using this template: https://github.com/jamesward/play-java-spring).
If I annotate the Application Controller with #Transactional it's working fine:
#org.springframework.stereotype.Controller
#Transactional
public class Application {
// ...
}
However, if I also extend from Play's Base Controller I get the following error:
[NoSuchBeanDefinitionException: No qualifying bean of type [controllers.Application] is defined]
Code:
#org.springframework.stereotype.Controller
#Transactional
public class Application extends play.mvc.Controller{
// ...
}
So for some reason the #TransactionalAnnotation combined with extends play.mvc.Controller leads to a NoSuchBeanDefinitionException.
Using either #Transactional OR extends play.mvc.Controller (not both combined) and Spring can instantiate the controller bean just fine.
How can I make them both work together?
This is the full stackstrace:
play.api.Application$$anon$1: Execution exception[[NoSuchBeanDefinitionException: No qualifying bean of type [controllers.Application] is defined]]
at play.api.Application$class.handleError(Application.scala:293) ~[play_2.10.jar:2.2.2]
at play.api.DefaultApplication.handleError(Application.scala:399) [play_2.10.jar:2.2.2]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.2]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.2]
at scala.Option.map(Option.scala:145) [scala-library.jar:na]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2.applyOrElse(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.2]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [controllers.Application] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:296) ~[spring-beans.jar:3.2.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1125) ~[spring-context.jar:3.2.3.RELEASE]
at Global.getControllerInstance(Global.java:21) ~[na:na]
at play.core.j.JavaGlobalSettingsAdapter.getControllerInstance(JavaGlobalSettingsAdapter.scala:46) ~[play_2.10.jar:2.2.2]
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$1$$anonfun$apply$1.apply(routes_routing.scala:57) ~[na:na]
at Routes$$anonfun$routes$1$$anonfun$applyOrElse$1$$anonfun$apply$1.apply(routes_routing.scala:57) ~[na:na]
Actually the controller itself should not be transactional, as transactionality is a concern of the service layer and not the presentation/web layer or the repository layer.
There are several reasons for this, for example the controller layer might trigger several business transactions. It's possible to make a controller transactional but it's not recommended practice, if you move the #Transactional annotation from the controller to the #Service layer, it will surely work.

Resources