Component/Integration Tests failing for #Async annotation in SpringBoot - spring

I'm facing an error while running Component/Integration Tests in SpringBoot project containing #Async annotation in one of the classes.
When I remove that #Async annotation it works fine.
My code briefly looks like below:
#Configuration
#EnableAsync
public class MyAsyncConfiguration {
#Bean
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
The #Async annotated method looks like below:
#Service
public class AsyncOperations {
#Async
public void doAsyncOperations() {
// Some business logic here
}
The code works totally fine when I hit my API from Postman. The Async operations happened asynchronously as expected, but when I run the Component/Integration Tests using mvn verify command, I get an error like below:
Caused by: Java.lang.IllegalStateException:Failed to introspect class [className] from ClassLoader [jdk.internal.loader.ClassLoader]
Caused by: java.lang.ClassFormatError: Method "$jacocoData" in class (AsyncOperations) has illegal signature

Related

How to avoid a second Instantiation of a spring bean in child test context

I created an Embedded Sftp server bean for my integration tests, i hooked the startup and the shutdown of the server respectively with the afterPropertiesSet and destroy life cycles
public class EmbeddedSftpServer implements InitializingBean, DisposableBean {
//other class content
#Override
public void afterPropertiesSet() throws Exception {
//Code for starting server here
}
#Override
public void destroy() throws Exception {
//Code for stopping server here
}
}
here my config class
#TestConfiguration
public class SftpTestConfig {
#Bean
public EmbeddedSftpServer embeddedSftpServer() {
return new EmbeddedSftpServer();
}
//Other bean definitions
}
Now when i inject the bean in my test classes like the following :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleOneIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleTwoIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#SpringBatchTest
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleThreeIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
And i run all the test classes simultaneously, i found out that for the test classes annotated with #ExtendWith(SpringExtension.class), it's the same context that is used (which is understandable since i guess spring cache it) and therefore the bean lifecycle methods are not executed again, but to my surprise, for the class annotated with #SpringBatchTest i noticed that the life cycle hooks of the bean are executed again! Which is a behavior that is not convenient since i want the application context to start the server one time for all tests and close it at the end of those tests (which is the case if i use only #ExtendWith(SpringExtension.class) for all my test classes).
N.B. : I need to use #SpringBachTest for my ExampleThreeIT test class.
I think you are hitting this issue: https://github.com/spring-projects/spring-batch/issues/3940 which has been fixed in v4.3.4/4.2.8. Upgrading to one of these versions should fix your issue.

#webMvcTest is not excluding and loading beans marked as #Repository

I've a #RestController which has only one dependency in field #Autowire
that dependency is #component, that component Class definition has some autowired fields which are #service and those services have some #repositories.
In this whole flow I've kafka, Quartz, Cassandra and DB2
So when I was creating a Unit test case for my controller, I dont want to setup whole application. so I decided to use #webMvcTest and used #MockBean on my only one dependency of controller class.
But my Test is throwing and exception because its trying to create a Dao bean, which is marked as #repository.
#ActiveProfiles("test")
#WebMvcTest(controllers = MyControllerTest .class)
class MyControllerTest {
#MockBean
MyControllerDependency dependency;
#Autowired
MockMvc mockMvc;
#Test
void test_something() throws Exception {
assert(true);
}
}
Here is oversimplified version of code
#Component
class MyControllerDependency {
#AutoiWired
MyCustomService service;
}
#Service
class MyCustomService{
#Autowired
MyCustomDao dao;
}
#Repository
class MyCustomDao{
#Autowired
private JdbcTemplate template;
}
I'm getting following exception in test.
Exception
***************************
APPLICATION FAILED TO START
***************************
Description:
Field template in com.....MyCustomDao` required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.
Question is, When I'm using #WebMvcTest slice and already mocking the only required dependency MyControllerDependency then why spring test context is trying to load MyCustomDao which is annotated as #Repository.
I can do integration testing with SpringbootTest & AutoconfigureMockMVC, but for writing Junit test just for controller, I need to use WebMvcTest slice. which is creating a problem.
I ran into a similar problem where I want to test only my controller using #WebMvcTest, but the spring context was trying to create nondependent spring beans and was failing as below.
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TestController' defined in file ...
Solution: load only the controller your testing for an example like #ContextConfiguration(classes = DemoController.class).
Also, find below a complete sample
#WebMvcTest
#ContextConfiguration(classes = DemoController.class)
public class DemoControllerTest {
#Autowired
MockMvc mockMvc;
#MockBean
DemoService demoService;
#Test
public void testGetAllProductCodes_withOutData() throws Exception {
when(productCodeService.getAllProductCodes()).thenReturn(new ArrayList<ProductCodes>());
mockMvc.perform(MockMvcRequestBuilders.get("/services/productCodes")).andExpect(MockMvcResultMatchers.status().isNoContent());
}
}
}
Do you have any #ComponentScan("...") annotation active on your #SpringBootApplication?
As described in Spring Boot Reference Documentation:
Another source of confusion is classpath scanning. Assume that, while you structured your code in a sensible way, you need to scan an additional package. Your application may resemble the following code:
#SpringBootApplication
#ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
Doing so effectively overrides the default component scan directive with the side effect of scanning those two packages regardless of the slice that you chose. For instance, a #DataJpaTest seems to suddenly scan components and user configurations of your application. Again, moving the custom directive to a separate class is a good way to fix this issue.
One solution is to create a seperate #Configuration that is annotated with #ComponentScan. When creating a #WebMvcTest the configuration (and its component scan is ignored).
#Configuration
#ComponentScan("com.example.another")
public class DbConfig {
}
This usually happens when you have explicit #ComponentScan annotation on the spring boot main application class.
The #ComponentScan annotation suppresses the default component scanning mechanism that happens with #Webmvctest where it scans up the package hierarchy and apply excludeFilters to find only controller and its related classes.
When you mock your bean using #MockBean annotation you should define what that mocked bean should do when you call it's method, you usually do this using when in Mockito. In your case it can be done this way:
#ActiveProfiles("test")
#WebMvcTest
class MyControllerTest {
#MockBean
MyControllerDependency dependency;
#Autowired
MockMvc mockMvc;
#Test
void test_something() throws Exception {
when(dependency.sample()).thenReturn("Hello, Mock");
mockMvc.perform(get("/api/test/restpoint")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
}
At this line:
when(dependency.sample()).thenReturn("Hello, Mock");
Instead of dependency.sample() you should put whatever method of MyControllerDependency class your controller call when you send a GET request to /api/test/restpoint path and with thenReturn("Hello, Mock") you define what is the mocked output of that method (when it gets called by your controller in your Unit test).

How to correctly setup Spring Boot 2.2 REST full integration test with JUnit5

I have simple controller, facade, set of services and repository for my H2 DB for Customer entity. It works fine but I need an E2E integration test with whole application context. And with JUnit5.
I've tried many setups, many times got errors like found multiple declarations of #BootstrapWith for test. Finally I made it work, but there is an issue with Spring JPA CustomerRepository which is an interface. I've never tried to use it like this so I have no experiance with it. But simply speaking, it won't create any bean of this type since it's just an interface. As the spring boot application, it works this way, but in the integration test it won't.
The error is still the same: Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [xxx.CustomerRepository].
This is my test setup:
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#ContextConfiguration(classes = {
CustomerController.class,
CustomerFacade.class,
CustomerTerminationService.class,
CustomerRepository.class
})
#WebMvcTest(CustomerController.class)
#Import(CustomerRepository.class)
class CustomerFullIntegrationTest {
#Autowired
private MockMvc mockMvc;
#Test
void getCustomer() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.get("/customer/1")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.lastName").exists())
.andExpect(jsonPath("$.products").isNotEmpty());
}
}
This is my repository:
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
This is my Application:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(ProfApplication.class, args);
}
}
Controller, facade, services are irrelevant.
Can you tell me, what exactly am I doing wrong?

jUnit: No primary or default constructor found for interface com.querydsl.core.types.Predicate

I have a restcontroller inside a spring-application returning a list of objects...
#GetMapping
#Override
public ResponseEntity readAll(#QuerydslPredicate(root = Entity.class) Predicate predicate, Pageable pageable){
...
}
If I run it, everything works fine. I can filter the request by pageable and predicate. But if I run the junit test, it fails...
#Test
public void readAllTest(){
MockMvcBuilders.standaloneSetup(*myController*)
.build().perform(MockMvcRequestBuilders.get(*myUri*)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
Getting the following Errormessage...
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface com.querydsl.core.types.Predicate
Does anybody know how to test a restcontroller with a Pageable and Predicate?
Try to add on your test class annotation #Import(QuerydslWebConfiguration.class). It adds controller argument resolver for com.querydsl.core.types.Predicate into spring context.
But after you'll face with an exception like:
No primary or default constructor found for interface org.springframework.data.domain.Pageable.
There is annotation, loads argument resolvers for both these interfaces. org.springframework.data.web.config.EnableSpringDataWebSupport
Addapted for your test class:
#RunWith(SpringRunner.class)
#WebMvcTest(*myController*.class)
#EnableSpringDataWebSupport
public class ControllerTest{
#Test
public void readAllTest(){
MockMvcBuilders.standaloneSetup(*myController*)
.build().perform(MockMvcRequestBuilders.get(*myUri*)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
For now, you just need to setup mockMvc with ApplicationContext, like
#RunWith(SpringRunner.class)
#SpringBootTest
public class ControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void readAllTest() throws Exception {
MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build()
.perform(MockMvcRequestBuilders.get("YOUR_URI")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-server
If anyone is facing this not just with tests, but also with normal application run, this might be due to web application not being configured by Spring Boot. For instance my project had a SwaggerConfig extending WebMvcConfigurationSupport:
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
// Docket bean
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
}
}
I removed the inheritance and manual resource handlers, and it works fine now.
Note: In addition to WebMvcConfigurationSupport, things like #EnableWebMvc & WebMvcConfigurer might also lead to Spring Boot's web autoconfiguration not being used.
Sources: Another SO Answer, Swagger Issue comment

Pageable in controller not instantiated

I have a spring-boot app with controller that uses Pageable as an argument to one of its methods. It works fine. But I want to use spring-contract-verifier to generate and execute tests.
Base class for tests looks like this:
public class MvcTest {
#Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new Controller());
}
}
The exception I'm getting is:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:99)
I searched SO and I found out that I should add
#EnableSpringDataWebSupport
to my configuration class. I created a class:
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
public class WebConfig {
}
However, I'm not sure how to tell contract verifier to pull this class into configuration.
Thanks
Its the wrong pagable: use org.springframework.data.domain.Pageable instead of java.awt.print.Pageable.
you need to enable
#EnableSpringDataWebSupport
or
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
#see: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web

Resources