In this chapter we are going to create service layer which exposes backend functionality.
Your first task is to implement a simple PersonService
. This service will manage basic CRUD operations for a Person
entity. After implementing the service, you will write test cases to verify the service’s functionality.
-
Create a package named
com.capgemini.training.todo.task.service
. -
Within this package, create a class named
PersonService
. -
Use annotations like
@RestController
,@RequestMapping
,@GetMapping
,@PostMapping
, and@DeleteMapping
to define your RESTful service. -
Map the service to the
/person
path, so it will be availablu underhttp://localhost:8080/person/
@RestController @RequestMapping("person") @RequiredArgsConstructor public class PersonService { // Your code here }
-
Implement CRUD operations to list, get, create and delete a
Person
using the use cases implemented in the business logic layer. -
Use annotations like
@PathVariable
to map the path variable to the method parameter. -
Use annotation
@RequestBody
to define the parameters representing the request parameters (e.g. for thePOST
method)final FindPersonUc findPersonUc; final ManagePersonUc managePersonUc; @GetMapping("/{id}") PersonEto findPerson(@PathVariable("id") @NotNull Long id) { return findPersonUc.findPerson(id) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Person with id " + id + " does not exist.")); } // TODO annotate List<PersonEto> findAllPersons() { // TODO Implement me! return null; } // TODO annotate PersonEto savePerson(PersonEto personEto) { // TODO Implement me! return null; } // TODO annotate void deletePerson(@NotNull Long id) { // TODO Implement me! return null; }
The service method implemented by findPerson
can be accessed under following url using GET
request:
http://localhost:8080/person/{id}
Test the implemented services using Postman (or your favourite testing tool)
The other methods can be tested in the similar way
-
Annotate the test class using
@WebMvcTest
. It will define a Spring MVC test that focuses only on Spring MVC components. -
Use
MockMvc
to simulate HTTP requests. Use the factory methods (likeget
,post
,status
) fromMockMvcResultMatchers
-
Use Mockito to mock the service dependencies from the logic layer.
-
Use
@MockBean
to inject the mocked dependencies. -
Below is an example of how to test the
findAllPersons
method:@WebMvcTest(PersonService.class) public class PersonServiceTest { @Autowired private MockMvc mockMvc; @MockBean private FindPersonUc findPersonUc; @Test public void findPerson_ShouldReturnPerson() throws Exception { Long personId = 1L; PersonEto person = PersonEto.builder().id(personId).version(1).email("[email protected]").build(); given(findPersonUc.findPerson(personId)).willReturn(Optional.of(person)); mockMvc.perform(get("/person/{id}", personId).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(person.id())) .andExpect(jsonPath("$.email").value(person.email())); } // Here other tests }
-
Write test cases for each CRUD operation you have implemented in the
PersonService
After you have successfully completed the PersonService
, proceed to implement TaskItemService
and TaskListService
with their respective test cases using the use cases you have implemented in the logic layer.
Follow the same steps as in the previous service for both TaskItemService
and TaskListService
. Ensure you implement CRUD operations and write corresponding test cases for each method.
Usually the validation of the parameters should be performed on the service layer to prevent requests which are not valid.
In this task you can add input validation to your services. Utilize annotations like @Validated
in your service class and @Valid
and @NotNull
in your service methods to enforce constraints on incoming data.
Please follow Bean validation using Hibernate Validator in Task Service - Business Logic Layer for more details.
When writing your test cases, you might need to send JSON payloads. Use Jackson’s ObjectMapper
to serialize Java objects into JSON strings:
ObjectMapper objectMapper = new ObjectMapper();
PersonEto newPerson = PersonEto.builder().id(null).version(0).email("[email protected]").build();
String newPersonJson = objectMapper.writeValueAsString(newPerson);
mockMvc.perform(
post("/person/").contentType(MediaType.APPLICATION_JSON).content(newPersonJson))
.andExpect(...)
...
)
If you encounter InvalidDefinitionException
with Java 8 date/time types, like java.time.Instant
, you can fix this by registering the JavaTimeModule
with your ObjectMapper
:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
This allows your ObjectMapper
to correctly serialize and deserialize Java 8 date/time types.