Testing servlets with spring beans and embedded jetty - spring

What is the best way to setup embedded jetty to use Spring test context for unit tests?
We are having problems testing legacy servlets which were sprinkled up with Spring beans like this:
public class MyServlet extends HttpServlet {
#Autowired
private MachineDao machineDao;
void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, this.getServletContext());
}
...
We are using embedded jetty (v9.2) to start Server with a particular servlet to test in spring test.
#ClassRule
public static TestServer server = new TestServer(MyServlet.class);
Problem is that spring test context is not accessible in jetty so processInjectionBasedOnServletContext(...) fails.
We also want to test that .jsp is rendered correctly, so using just new MyServlet() and MockHttpServletRequest & MockHttpServletResponse approach is not really a viable option.
We are using Spring 4.11 (not MVC) with java config & annotations.
Any help much appreciated!

Related

Are the project beans already instantiated when we try to run a junit test in spring boot

I am new to Spring and spring boot.
For my spring boot application which is a rest controller, I have some beans along with my data source.
I use my data source to create jdbc template. Now when I am in my rest controller code, I have all these beans #Autowired and they work perfectly fine.
My query is regarding the junit testing part.
When I write my test code inside src/test/java and when I execute my test class within IDE, are the beans defined in my src/main/javacode, instantiated before test case execution?
You might use the same container, or instantiate another container particularly for testing purposes, for which you'll provide a configuration of that other Spring Container separately:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:test-context.xml")
public class SomeClassTest{...}
However, you can also enable support for loading your Application Context and then use the #Autowired fields in your JUnit fixtures, which also works fine too:
#RunWith(SpringRunner.class)
public class SomeTestClass {
....
#Autowired
ApplicationContext context;
....
}
From here, you can get any bean you wish.

What Is the Correct Way To Use AbstractReactiveWebInitializer

I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method

WebApplicationContext is always null in a spring boot test

My test class looks like this
#SpringBootTest(webEnvironment=WebEnvironment.MOCK)
public class sampleClassTest{
#Autowired
private WebApplicationContext wac;
}
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
In the setup method, wac is always null. From spring boot documentation, #SpringBootTest(webEnvironment=WebEnvironment.MOCK) always created a mock WebapplicaitonContext.
So I would expect it get autowired in the code above which doesn't happen.
Can someone tell me how to go about creating a webapplicationContext in this case so that it's not null like in my case ?
UPDATE
I am running spring boot tests invoking them from a class with springboot annotation.
Both test (springboottest) and calling class (springboot) application are in the same spring boot project under src/main/java.
I have nothing under src/main/test. I have done in this way because if classes from src/main/java want to call a test class then, it isn't really a test class.
Now, the problem is that I can't use runWith(SpringRunner.class) in springbootTest class. If I did that to get a mock webApplicationContext then, it gives me this error:
javax.management.InstanceAlreadyExistsException: org.springframework.boot:type=Admin,name=SpringApplication
I am not sure how to do about this.
To use #SpringBootTest you need to use Spring Framework's test runner. Annotate your test class with #RunWith(SpringRunner.class).
If someone is struggling with this issue in 2022 - please keep my defined precondions in mind. If you are using #SpringBootTest with defined port and constructor auto-wiring of the test class, the application context might be null.
It seems that the constructor dependency injection is eager and the cache aware context delegate of Spring is searching for a web application context which is no available yet. If you use field auto-wiring your test might run in a deterministic manner.
Whoever is facing this issue, make sure your spring boot starter parent version is compatible with spring cloud version in pom.xml
I was facing same issue, i resolved it by doing same.

How to ensure load time weaving takes place for Eclipselink when using SpringBootTest with other tests running beforethe Spring one

I'm using Spring Rest Docs to generate documentation for my REST services. This involves running unit(strictly integration) tests that run against a live Spring Boot Container that is kicked off by the test. The test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MySpringConfiguration.class)
#WebAppConfiguration
public class ApiDocumentation {
private MockMvc mockMvc;
#Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
#Autowired
private WebApplicationContext context;
#Autowired
private ObjectMapper objectMapper;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void testSomething() throws Exception {
}
}
The application uses JPA with EclipseLink for the EntityManager implementation.
When I run the test standalone in my IDE or as the only test present when I run a Maven build using the maven-surefire-plugin everything works fine.
However it's not the only test I want to run in the suite. As soon as I run other tests in the suite I come across the issue mentioned here, namely
"Spring's agent does not initialize the persistence context until the application accesses the Spring context. If the application has already triggered the loading of the persistent class before accessing the Spring context, weaving will not occur."
and get errors like this:
Exception Description: The method [_persistence_set_someField_vh] or [_persistence_get_someField_vh] is not defined in the object [mypackage.MyEntity].
So what do people normally do to get around this ? Run SpringBootTest classes in a different module to unit tests that access entities ?
As far as I concerned problem caused by dynamic weaving, if you make it static it should work proper. Possibly it could help you
Another solution could be to disable dynamic weaving in that particular test using eclipselink.weaving JPA property.
See this question and its answers: #SpringBootTest interferes with EclipseLink dynamic weaving

Integrating Spring XML beans from external jar into a CDI application

I have a new CDI Java EE application running on WebSphere. Now I want to use an existing module (.jar) in my CDI project, however the existing module uses Spring with Spring annotations and an Spring XML configuration file with additional bean definitions in it. Normally I would just import the Spring XML in my project, but in the CDI application this will not work.
I tried to load the Spring XML using JBoss Seam, like so:
#Produces
#SpringContext
#Configuration(locations = "classpath*:external-spring--context.xml")
ApplicationContext context;
But the context is null? I cannot realy find good examples on how to do this, help is much appreciated :)
I solved it by adding an CDI producer that will create the Spring context using the spring XML file:
public class SpringBeansFactory {
#Inject
ApplicationContext context;
#Produces
public BusinesService getBusinessService() {
return context.getBean(BusinesService.class);
}
}
class SpringContextFactory {
#Produces
public ApplicationContext getApplicationContext() {
return new ClassPathXmlApplicationContext("classpath:spring-context.xml");
}
}

Resources