Spock spring module results in NoSuchMethodError when running the test - java

I am using the following spock dependency
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.0-groovy-2.4</version>
<scope>test</scope>
</dependency>
I wrote this test
class JsonIncludeResolverTest extends Specification {
JsonIncludeResolver uut;
def "test"(){
expect:
true
}
}
the test succeeds
Now I want to use Spring to inject JsonIncludeResolverwithin the specification.
JsonIncludeResolver is annotated with #Component and is injected successfully when running the application. Both tests and component are placed in the same maven module.
I read about spring-spock here: How to inject spring beans into spock test
Therefore I added this dependency version
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
after adding this dependency running the s
Exception in thread "main" java.lang.NoSuchMethodError: org.spockframework.util.ReflectionUtil.isAnnotationPresentRecursive(Ljava/lang/Class;Ljava/lang/Class;)Z
at org.spockframework.spring.SpringExtension.isSpringSpec(SpringExtension.java:83)
at org.spockframework.spring.SpringExtension.visitSpec(SpringExtension.java:59)
at org.spockframework.runtime.ExtensionRunner.runGlobalExtensions(ExtensionRunner.java:46)
at org.spockframework.runtime.ExtensionRunner.run(ExtensionRunner.java:40)
at org.spockframework.runtime.Sputnik.runExtensionsIfNecessary(Sputnik.java:88)
at org.spockframework.runtime.Sputnik.getDescription(Sputnik.java:55)
at com.intellij.junit4.JUnit4IdeaTestRunner.getDescription(JUnit4IdeaTestRunner.java:78)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:50)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
I then tried to use the proper annotations
#ContextConfiguration
class JsonIncludeResolverTest extends Specification {
#Autowired
JsonIncludeResolver uut;
def "test"(){
expect:
uut != null
}
}
Still the result is the above exception.
The groovy test is under /src/test/groovy which is a working as you could see in the first successful run of the test.

Try upgrading Spock to 1.1 as follows:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
org.spockframework.util.ReflectionUtil.isAnnotationPresentRecursive() has been introduced since version 1.1 by this commit.

Related

Spring Boot 2.6.4 -> 2.6.6 : strange NullPointerException within Logback when logging a mock Exception

while upgrading from Spring Boot 2.6.4 to 2.6.6 , one of my tests (written in Kotlin), fails :
#Test
fun shouldLogProperMessageIfNotAbleToHitAPI() {
val configValidator = ConfigValidator(GitHubCrawlerProperties(SourceControlConfig(url = "someIncorrectURL",organizationName="someOrg")),mockRemoteSourceControl)
`when`(mockRemoteSourceControl.validateRemoteConfig("someOrg")).thenThrow(NoReachableRepositories("problem !",mock(Exception::class.java)))
val validationErrors=configValidator.getValidationErrors()
assertThat(validationErrors).hasSize(1);
}
the build passes with Spring Boot 2.6.4. It works in Spring Boot 2.6.6 when I run the test individually in my IDE, but fails during the maven build.
the stacktrace was not showing by default, but after surrounding the call by a try/catch, I am able to get it, and it points to Logback :
java.lang.NullPointerException: null
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:99)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:89)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:62)
at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
at ch.qos.logback.classic.Logger.error(Logger.java:538)
at com.societegenerale.githubcrawler.ConfigValidator.getValidationErrors(ConfigValidator.kt:48)
Logback version doesn't seem to change, I still get v 1.2.11 .
Looking at Logback source code, in ThrowableProxy :
if (GET_SUPPRESSED_METHOD != null) {
// this will only execute on Java 7
Throwable[] throwableSuppressed = extractSupressedThrowables(throwable);
if (throwableSuppressed.length > 0) {
List<ThrowableProxy> suppressedList = new ArrayList<ThrowableProxy>(throwableSuppressed.length);
for (Throwable sup : throwableSuppressed) {
...
note : I build with Java 11, so the comment saying in Logback source code that this will only execute on Java 7 , seems wrong.
It seems that throwableSuppressed is null, and I get the NPE when throwableSuppressed.size is called.
The test passes if instead of using a mock in NoReachableRepositories("problem !",mock(Exception::class.java)) , I use NoReachableRepositories("problem !",Exception())
I realize it's probably better to use a real Exception rather than a mock, so my problem is solved in a way (after spending 2 hours on this..).
However, I am curious : what could cause this issue after upgrading to Spring Boot 2.6.6 which should be a a minor change ?
This issue was introduced in logback:1.2.11 by this commit. It is tracked in this Jira ticket.
Logback was upgraded to 1.2.11 from spring boot 2.6.5, you can refer to this changelog. So you would have encountered this same error if you upgraded to 2.6.5.
What we can do now is override the version of logback to 1.2.10 by adding this line in build.gradle file.
ext["logback.version"] = "1.2.10"
If you're using Maven dependencyManagement section for Spring Boot dependencies instead of the starter parent then you can try this:
<!-- ... -->
<dependencyManagement>
<dependencies>
<!-- temp. override logback version for https://jira.qos.ch/browse/LOGBACK-1623-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
<!-- ... -->
Update: Spring Boot 2 latest version (2.7.5) is still using logback:1.2.11.

Swagger 2 Issue spring Boot

I'm facing issue with Swagger Integration in Spring Boot. Have a look at the code and error snippet.
------------------POM--------------------
<properties>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
</properties>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>${swagger.version}</version>
</dependency>
-----------------App class--------------
#SpringBootApplication
#EnableSwagger2
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ServletPocProducerApplication.class, args);
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
Stack Trace
org.springframework.context.ApplicationContextException: Failed to start bean
'documentationPluginsBootstrapper'; nested exception is
java.lang.NullPointerException: Cannot invoke
"org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.toString()"
because the return value of
"springfox.documentation.spi.service.contexts.Orderings.patternsCondition(springfox.docume
ntation.RequestHandler)" is null
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.13.jar:5.3.13]
How do I fix this??
I solved it by adding "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" in application.properties.
For a long time I have tried to solve this problem and solution for this is:
a) adding this to application.properties:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
b) adding this to application.yaml(or application.yml):
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
I know this does not solve your problem directly, but consider moving to springdoc. Springfox is so buggy at this point that is a pain to use. I've moved to springdoc 2 years ago because of its Spring WebFlux support and I am very happy about it. Additionally, it also supports Kotlin Coroutines, which I am not sure Springfox does.
If you decide to migrate, springdoc even has a migration guide.
For the integration between spring-boot and swagger-ui, add the library to the list of your project dependencies (No additional configuration is needed):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
The Swagger UI page will then be available at
http://server:port/context-path/swagger-ui.html and the OpenAPI
description will be available at the following url for json format:
http://server:port/context-path/v3/api-docs
server: The server name or IP
port: The server port
context-path: The context path of the application
Adding this "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" to your application.properties file solves the problem.
It's what i used and i saved me alot of trouble.
My suggestion is when you are using spring-boot then it is better to use spring boot dependency for swagger. So, spring-boot will take care of your default settings.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>...</version>
</dependency>

resilience4j-spring-boot-2 annotations (#Retry, #CircuitBreaker...) are completely ignored

I spent a whole day trying to find why this does not work so I think it might be useful if I share the question and the answer.
The Resilience4j library provides an elegant annotation-based solution from Spring Boot 2. All you need to do is just annotate a method (or a class) with one of the provided annotations, such as #CircuitBreaker, #Retry, #RateLimiter, #Bulkhead, #Thread and the appropriate resilience pattern is automagically added.
I added the expected dependency to the Maven pom.xml:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>${resilience4j.version}</version>
</dependency>
Now the compiler is happy, so I can add the annotations:
...
import org.springframework.stereotype.Service;
import io.github.resilience4j.retry.annotation.Retry;
...
#Service
public class MyService {
...
#Retry(name = "get-response")
public MyResponse getResponse(MyRequest request) {
...
}
}
The program compiles, runs, however the annotations are completely ignored.
According to the resilience4j-spring-boot2 documentation:
The module expects that spring-boot-starter-actuator and spring-boot-starter-aop are already provided at runtime.
So the whole trick is to add also the missing dependencies to the Maven pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Test class in spring dosn't work properly

I would like to test my controller class. But I couldn't manage to run springBootTest class. My project written in spring boot. We are writing REST API using spring boot.
When I try to excute following test class. I still get following line from terminal.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
/*
*
* #A Sabirov Jakhongir
*
*/
#SpringBootTest
#WebMvcTest
public class PrivilegesControllerTest {
#Autowired
private PrivilegesController privilegesController;
#Test
public void add() {
assertThat(privilegesController).isNotNull();
}
}
I put here all needed dependency for testing from my project.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
What might be cause of not working of test Class.
With Junit5 and #SpringBootTest will load the full application, I had faced the same issue before, you can find details about the question here and answer here.
The solution for this is to use your test without #SpringBootTest.
The solution to your test class is as below.
#ExtendWith(MockitoExtension.class)
public class PrivilegesControllerTest {
#InjectMocks
private PrivilegesController privilegesController;
#Test
public void add() {
assertThat(privilegesController).isNotNull();
}
}
You can also use #ExtendWith(SpringExtension.class) instead of #ExtendWith(MockitoExtension.class)
To test spring boot application is creating your controller, use #SpringBootTest annotation, and to test the behavior or responsibility of the controller is better to use #WebMvcTest. No need to annotate both the annotation to one class.
There are two cases to test the controller's responsibility.
With running Server
Without Server
For 1st Case, you can use #SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) to start a server with a random port.
For 2nd Case, use #WebMvcTest for testing the web layer.
All the test cases are written with the following signature:
#Test
public void test_Name() throws Exception {
//your test definition
}
You can also read the Official Documentation of Spring Boot https://spring.io/guides/gs/testing-web/

Unable to resolve Spring AOP types in Spring boot project

I created a new Spring boot project and was trying to implement some AOP concerns.
However, my code simply doesn't recognize the Classes from AOP. I checked and confirmed that spring-aop-5.0.7.RELEASE.jar is indeed present in Maven dependencies and JRE Runtime Libraries.
My code yet is very simple:
#Aspect
#Component
public class LoggingAspect {
#Around("execution(* com.springboot.service.*(..)) ")
public Object logAround(ProceedingJoinPoint joinPoint ) throws Throwable {
}
}
But in this code Aspect cannot be resolved to a type and same for other annotation and classes like Joinpoint and #Around. Other Spring annotation and classes work perfectly fine, ex. #Component, #Controllers etc.. and the project in itself runs fine without AOP.
I have already tried cleaning and re-building the project.
What can I be missing. Any help is appreciated.
#Aspect is located in the spring-aspects.jar or one of it's dependencies. add it as dependency:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
The #Aspect and #Around (and other such) annotations are part of the org.aspectj aspectjweaver artifact which is an optional compile dependency in your version of spring-aop.
You have to include it explicitly
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>

Categories