I want to test the next controller class using Jersey Test in my Spring Boot application.
Controller Class
@RestController
@RequestMapping("/api/v1/demo")
public class UserController {
private final IUserService service;
public UserController(IUserService service) {
this.service = service;
}
@PostMapping(value = "/register", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserResponse> registerUser(@RequestBody @Valid UserRequest userRequest) {
UserResponse user = service.createUser(userRequest);
return ResponseEntity.status(CREATED).body(user);
}
}
UserRequest Class
public class UserRequest {
@NotBlank
private String firstName;
@NotBlank
private String lastName;
@NotBlank
private String email;
@NotBlank
private String password;
@NotBlank
private String afm;
@NotBlank
private String birthDate;
@NotBlank
private String mobileNumber;
@NotBlank
private String cardNumber;
//getters and setters, no arg and full arg constructor
build.gradle
plugins {
id 'java'
id 'groovy'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
}
group = 'com'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
runtimeOnly 'com.mysql:mysql-connector-j'
compileOnly 'jakarta.platform:jakarta.jakartaee-api:11.0.0-M4'
testImplementation 'org.eclipse:yasson'
implementation 'org.glassfish.jersey.test-framework:jersey-test-framework-core:4.0.0-M1'
implementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:4.0.0-M1'
implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:4.0.0-M1'
implementation 'org.glassfish.jersey.ext:jersey-bean-validation:4.0.0-M1'
implementation 'org.glassfish.jersey.inject:jersey-hk2:4.0.0-M1'
implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:4.0.0-M1'
implementation 'org.glassfish.jersey.core:jersey-client:4.0.0-M1'
implementation 'org.glassfish.jaxb:jaxb-runtime:4.0.5'
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1'
testImplementation 'net.bytebuddy:byte-buddy'
testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
testImplementation 'org.apache.groovy:groovy-all:4.0.17'
testImplementation 'org.spockframework:spock-core:2.4-M4-groovy-4.0'
testImplementation 'org.spockframework:spock-spring:2.4-M4-groovy-4.0'
testImplementation 'jakarta.json.bind:jakarta.json.bind-api'
testImplementation 'org.eclipse:yasson'
testImplementation 'io.github.joke:spock-mockable:2.3.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:mysql'
testImplementation 'com.github.dasniko:testcontainers-keycloak:3.4.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
task createTestResources() {
copy {
from 'env'
include 'test.properties'
into 'src/test/resources'
expand project.properties
}
}
clean.doFirst {
delete 'src/test/resources/test.properties'
}
Below you can see the test along with the configuration Test class
class UserControllerSpec extends ResourceSpecification {
static final String REGISTER_URL = '/api/v1/demo/register'
@Subject
UserController controller
IUserService service
@Override
protected createResource() {
service = Mock()
controller = new UserController(service)
}
def 'Successfully calling the preview endpoint'() {
given: 'a valid request'
def request = TestFixtures.createUserRequest()
and: 'an expected response'
def expectedResponse = TestFixtures.createUserResponse()
when: 'the controller is called'
def response = jerseyTest.target(REGISTER_URL).request().post(Entity.json(request))
then: 'response should have status 201'
response.getStatus() == 201
and: 'the cancel preview service is called once with the correct request parameters'
1 * service.createUser({
it.firstName == request.firstName &&
it.lastName == request.lastName &&
it.email == request.email &&
it.password == request.password &&
it.afm == request.afm &&
it.birthDate == request.birthDate &&
it.mobileNumber == request.mobileNumber &&
it.cardNumber == request.cardNumber
}) >> expectedResponse
and: 'the actual response should contain the expected values'
def actualResponse = response.readEntity(UserResponse.class)
actualResponse == expectedResponse
}
}
Configuration Class
abstract class ResourceSpecification extends Specification {
JerseyTest jerseyTest
JsonSlurper jsonSlurper
protected abstract createResource()
def setup() {
jerseyTest = new JerseyTest() {
@Override
protected Application configure() {
new ResourceConfig()
.register(createResource())
}
}
jerseyTest.setUp()
jsonSlurper = new JsonSlurper()
}
def cleanup() {
jerseyTest.tearDown()
}
}
My test fails with the next error:
Jul 29, 2024 8:37:50 PM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer <init>
INFO: Creating GrizzlyTestContainer configured at the base URI http://localhost:9998/
Jul 29, 2024 8:37:50 PM org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider com.ebanking.system.controller.UserController registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider com.ebanking.system.controller.UserController will be ignored.
20:37:51.233 [Test worker] INFO org.hibernate.validator.internal.util.Version -- HV000001: Hibernate Validator 8.0.1.Final
Jul 29, 2024 8:37:51 PM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9998]
Jul 29, 2024 8:37:51 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Jul 29, 2024 8:37:51 PM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9998]
Too few invocations for:
1 * service.createUser({
it.firstName == request.firstName &&
it.lastName == request.lastName &&
it.email == request.email &&
it.password == request.password &&
it.afm == request.afm &&
it.birthDate == request.birthDate &&
it.mobileNumber == request.mobileNumber &&
it.cardNumber == request.cardNumber
}) >> expectedResponse (0 invocations)
Unmatched invocations (ordered by similarity):
None
Too few invocations for:
1 * service.createUser({
it.firstName == request.firstName &&
it.lastName == request.lastName &&
it.email == request.email &&
it.password == request.password &&
it.afm == request.afm &&
it.birthDate == request.birthDate &&
it.mobileNumber == request.mobileNumber &&
it.cardNumber == request.cardNumber
}) >> expectedResponse (0 invocations)
Unmatched invocations (ordered by similarity):
None
at org.spockframework.mock.runtime.InteractionScope.verifyInteractions(InteractionScope.java:110)
at org.spockframework.mock.runtime.MockController.leaveScope(MockController.java:95)
at com.ebanking.system.controller.UserControllerSpec.Successfully calling the preview endpoint(UserControllerSpec.groovy:33)
The ResourceSpecification
class works well with OpenLiberty applications but it seems to me that it does not happen the same with Spring Boot ones. What am I missing? Any suggestions?
I am not entirely sure that you can work Jersey Test framework along with Spring Boot MCV Controllers. Jersey Test framework is used on JAX-RS applications.