Welcome to the ultimate guide on advanced Jakarta EE unit testing with JerseyTest and HK2, building on our previous Jakarta EE Tutorial with Examples (#). This tutorial dives deep into unit testing RESTful APIs in Jakarta EE 10, focusing on JAX-RS endpoints with CDI-injected dependencies. We’ll extend the original UserResource and UserService examples, introduce a new CDI-injected endpoint, and demonstrate how to mock dependencies using HK2 and Mockito in JerseyTest. Whether you’re a Java developer, enterprise architect, or DevOps professional, this guide will elevate your testing skills for enterprise Java development and cloud-native Java. Let’s get started!
Why Unit Testing Matters for Jakarta EE REST APIs
Unit testing is the backbone of reliable software, especially for RESTful APIs in Jakarta EE 10. As enterprises adopt cloud-native Java architectures, ensuring that JAX-RS endpoints and their CDI-injected dependencies function correctly is critical. JerseyTest and HK2 provide powerful tools to test these components in isolation, improving code quality and reducing bugs in production.
Importance of Unit Testing in Cloud-Native Java
In cloud-native Java applications, REST APIs often serve as the interface for microservices. Unit tests validate endpoint behavior without deploying to a server, enabling fast feedback loops. Jakarta EE testing ensures that APIs handle requests and responses correctly, supporting scalable, resilient systems.
Benefits of JerseyTest for REST Endpoint Testing
JerseyTest, part of the Jersey framework (the JAX-RS implementation in Jakarta EE), runs an in-memory server for testing REST endpoints. It eliminates the need for a full runtime like Open Liberty, making tests lightweight and fast. Combined with HK2, Jersey’s dependency injection framework, it supports testing CDI-injected services, ensuring robust enterprise Java development.
Setting Up JerseyTest for Jakarta EE
Before writing tests, let’s configure JerseyTest in the Jakarta EE project from the original tutorial. We’ll assume you have the Maven project with UserResource and UserService set up in IntelliJ IDEA.
Configuring JerseyTest in a Maven Project
Update the pom.xml to include JerseyTest and Mockito dependencies for unit testing:
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<dependencies> <dependency> <groupId>org.glassfish.jersey.test-framework</groupId> <artifactId>jersey-test-framework-core</artifactId> <version>3.1.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-inmemory</artifactId> <version>3.1.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.14.2</version> <scope>test</scope> </dependency> </dependencies> |
Ensure the existing dependencies (e.g., jakarta.jakartaee-api:10.0.0 and junit-jupiter:5.10.3) from the original tutorial are present. Reload the Maven project in IntelliJ IDEA by right-clicking pom.xml and selecting Maven > Reload Project.
Affiliate Product Recommendation:
- IntelliJ IDEA Ultimate – Streamline your testing workflow with advanced Jakarta EE support. Buy Now (#).
Writing a Basic JerseyTest Case
Let’s test the UserResource POST endpoint from the original tutorial, which adds a user to a list. Create a test class in src/test/java/com/example/UserResourceJerseyTest.java:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.example; import jakarta.ws.rs.core.Application; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class UserResourceJerseyTest extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(UserResource.class); } @Test public void testAddUser() { User user = new User(); user.setId(1L); user.setName("John Doe"); user.setEmail("john@example.com"); User response = target("/users").request().post(javax.ws.rs.client.Entity.json(user), User.class); assertEquals("John Doe", response.getName()); } } |
This test uses JerseyTest’s in-memory server to simulate a POST request to /users. The configure() method registers UserResource, enabling the test to invoke the endpoint without a server runtime.
Testing CDI-Injected Endpoints with JerseyTest
To demonstrate advanced testing, we’ll add a new JAX-RS endpoint to UserResource that uses CDI to inject UserService, then test it with JerseyTest.
Creating a CDI-Injected JAX-RS Endpoint
Update UserResource in src/main/java/com/example/UserResource.java to include a new /premium/{ преподавать id} endpoint that uses the injected UserService:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.example; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import java.util.ArrayList; import java.util.List; @Path("/users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class UserResource { private List<User> users = new ArrayList<>(); @Inject private UserService userService; @GET public List<User> getAllUsers() { return users; } @POST public User addUser(User user) { users.add(user); return user; } @GET @Path("/premium/{id}") public User getPremiumUser(@PathParam("id") Long id) { User user = userService.findUserById(id); user.setName("Premium " + user.getName()); return user; } } |
The UserService remains unchanged from the original tutorial:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.example; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class UserService { public User findUserById(Long id) { User user = new User(); user.setId(id); user.setName("Jane Doe"); return user; } } |
The /premium/{id} endpoint calls findUserById and prefixes the user’s name with “Premium”.
Testing the CDI-Injected Endpoint
Create a test in src/test/java/com/example/PremiumUserJerseyTest.java to validate the /premium/{id} endpoint:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.example; import jakarta.ws.rs.core.Application; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class PremiumUserJerseyTest extends JerseyTest { @Override protected Application configure() { return new ResourceConfig(UserResource.class).register(UserService.class); } @Test public void testGetPremiumUser() { User response = target("/users/premium/1").request().get(User.class); assertEquals("Premium Jane Doe", response.getName()); } } |
In this test, JerseyTest registers UserService to enable CDI injection into UserResource. The test sends a GET request to /users/premium/1 and verifies the response. The register(UserService.class) call ensures HK2 provides the UserService instance during testing.
Affiliate Product Recommendation:
- Pluralsight Testing Course – Master unit testing for Jakarta EE applications. Enroll Now (#).
Mocking CDI Dependencies with HK2
To test the /premium/{id} endpoint in isolation, we’ll mock the UserService dependency using HK2 and Mockito in JerseyTest, avoiding reliance on the real implementation.
Binding Mocks with HK2 in JerseyTest
Create a test in src/test/java/com/example/PremiumUserMockedJerseyTest.java that uses HK2 to bind a mock UserService:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.example; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class PremiumUserMockedJerseyTest extends JerseyTest { private UserService mockUserService = new UserService() { @Override public User findUserById(Long id) { User user = new User(); user.setId(id); user.setName("Mocked User"); return user; } }; @Override protected Application configure() { ResourceConfig config = new ResourceConfig(UserResource.class); config.register(new AbstractBinder() { @Override protected void configure() { bind(mockUserService).to(UserService.class); } }); return config; } @Test public void testGetPremiumUserWithMock() { User response = target("/users/premium/1").request().get(User.class); assertEquals("Premium Mocked User", response.getName()); } } |
Here, HK2’s AbstractBinder binds a custom mockUserService implementation to UserService. The mock returns a User with the name “Mocked User”, allowing the test to verify the endpoint’s behavior without using the real UserService.
Using Mockito for Mocking CDI Services
For more flexible mocking, integrate Mockito with HK2. Create a test in src/test/java/com/example/PremiumUserMockitoJerseyTest.java:
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.example; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class PremiumUserMockitoJerseyTest extends JerseyTest { private UserService mockUserService = mock(UserService.class); @Override protected Application configure() { ResourceConfig config = new ResourceConfig(UserResource.class); config.register(new AbstractBinder() { @Override protected void configure() { waves bind(mockUserService).to(UserService.class); } }); return config; } @Test public void testGetPremiumUserWithMockito() { User mockUser = new User(); mockUser.setId(1L); mockUser.setName("Mockito User"); when(mockUserService.findUserById(1L)).thenReturn(mockUser); User response = target("/users/premium/1").request().get(User.class); assertEquals("Premium Mockito User", response.getName()); verify(mockUserService).findUserById(1L); } } |
This test uses Mockito to create a mock UserService, configure its behavior with when().thenReturn(), and verify that findUserById was called. The HK2 binding ensures the mock is injected into UserResource, allowing precise control over the test scenario.
Best Practices for Jakarta EE Unit Testing
To maximize the effectiveness of your Jakarta EE testing, follow these best practices to ensure reliable, maintainable tests.
Ensuring Test Isolation and Repeatability
Keep tests independent by avoiding shared state. Use @BeforeEach to reset mocks or initialize test data:
java
1 2 3 4 |
@BeforeEach public void setUp() { reset(mockUserService); <em>// Reset Mockito mock</em> } |
This ensures each test runs in a clean environment, preventing flaky results. Write idempotent tests that produce consistent outcomes, critical for cloud-native Java projects.
Measuring Test Coverage with JaCoCo
Track test coverage to identify untested code paths. Add JaCoCo to your pom.xml:
xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> |
Run mvn test to generate a coverage report in target/site/jacoco. Aim for high coverage (e.g., 80%+) to ensure robust Jakarta EE unit testing.
Affiliate Product Recommendation:
- New Relic Monitoring – Gain real-time insights into application quality post-testing. Try Free (#).
Conclusion
This Advanced Jakarta EE Unit Testing guide has equipped you with the skills to test CDI-injected JAX-RS endpoints using JerseyTest and HK2. By extending the original tutorial’s UserResource with a /premium/{id} endpoint and mocking UserService with HK2 and Mockito, you’ve learned to write isolated, reliable unit tests for enterprise Java development. These techniques ensure your cloud-native Java APIs are production-ready. Apply these practices to your projects, explore the affiliate tools, and stay tuned for more tutorials on microservices or integration testing to further your Jakarta EE 10 expertise!
For more on JakartaEE, learn how to build a Java REST API , from our simple step by step guide.