X1 Esports moves one step closer to becoming ultimate Rocket League fan hub with Octane.GG acquisition

By | 15/09/2022

Overview

In software evolution, testing each part of a program is crucial to assert that all individual parts are correct.

In the previous commodity nosotros covered some testing strategies, which yous can check it here.

A
unit of measurement
is the smallest testable part of the software and in object-oriented programming it’southward too called a method, which may vest to a super grade, abstract class or a child course. Either mode, unit of measurement tests are an of import step in the development stage of an application.

Here are some key reasons to not skip the proccess of creating unit tests :

  • They help to
    fix bugs
    early in the evolution cycle and salvage costs;
  • Understanding the code base of operations is essential and unit tests are some of the best style of enabling developers to larn all they can nearly the application and make changes chop-chop;
  • Skillful unit tests may besides serve every bit
    documentation
    for your software;
  • Lawmaking is more
    reliable
    and
    reusable
    because in social club to make unit testing possible, modular programming is the standard technique used;

Guidelines

  • Our test example should exist
    independent;
  • Exam only
    ane
    lawmaking at a time;
  • Follow
    clear
    and consequent naming conventions;
  • Since we are doing
    unit of measurement tests, nosotros demand to isolate dependencies. Nosotros volition use
    mocks
    for that. Mocks are objects that “fake”, or simulate, the real object behavior. This style we tin can control the behavior of our dependencies and examination only the code we want to test. Later on nosotros will do integration tests which uses no mocks and tests the actual behavior of all components and their integration.

Setting up our environment

In this project, we’ll exist working with a
Crud RESTful API
that we’ve developed using Spring Boot, if you want to know how we did that, you tin can click here.

For each operational endpoint, we’ll need to exam its
controller
and
service
past unitary approach, simulating its expected upshot and comparing with the actual event through a mock standpoint.

Our testing framework of selection will be
JUnit, it provides assertions to place test method, so be sure to include in your
maven
pom.xml
file :

<!-- Junit v --> <dependency>     <groupId>org.junit.jupiter</groupId>     <artifactId>junit-jupiter-api</artifactId>     <scope>test</scope> </dependency> <dependency>     <groupId>org.junit.jupiter</groupId>     <artifactId>junit-jupiter-engine</artifactId>     <scope>exam</scope> </dependency> <dependency>     <groupId>org.junit.platform</groupId>     <artifactId>junit-platform-launcher</artifactId>     <scope>exam</telescopic> </dependency>   <!-- Mockito extention --> <dependency>     <groupId>org.mockito</groupId>     <artifactId>mockito-junit-jupiter</artifactId>     <scope>examination</scope> </dependency>

Our tests will be grouped in separate folders, the following way :

Unit of measurement tests folder separation regarding controllers and services

Testing the Service layer

Our Service layer implements our logic and depends on our Repository so nosotros’ll need to
mock
its behavior through
Mockito
annotations and and so verify the code with known inputs and outputs.

For a quick epitomize of our Service layer lawmaking that will be tested, these are all our endpoints service classes pasted into 1 section of code :

@Service public class CreateUserService {      @Autowired     UserRepository repository;      public User createNewUser(User user) {         return repository.save(user);     } }  @Service public class DeleteUserService {      @Autowired     UserRepository repository;      public void deleteUser(Long id) {          repository.findById(id)                 .orElseThrow(() -> new UserNotFoundException(id));          repository.deleteById(id);     } }  @Service public course DetailUserService {      @Autowired     UserRepository repository;      public User listUser(Long id) {         return repository.findById(id)                 .orElseThrow(() -> new UserNotFoundException(id));     } }  @Service public class ListUserService {      @Autowired     UserRepository repository;      public List<User> listAllUsers() {         return repository.findAll();     } }  @Service public class UpdateUserService {      @Autowired     UserRepository repository;      public User updateUser(Long id, User user) {          repository.findById(id)                 .orElseThrow(() -> new UserNotFoundException(id));          user.setId(id);         render repository.save(user);     } }

Create a new user service

Starting with our
CreateUserService
class, nosotros’ll create a test course named
CreateUserServiceTest.

src/examination/java/com/usersapi/endpoints/unit/service/CreateUserServiceTest.java

@RunWith(MockitoJUnitRunner.class) public class CreateUserServiceTest {      @Mock     private UserRepository userRepository;      @InjectMocks     private CreateUserService createUserService;      @Examination     public void whenSaveUser_shouldReturnUser() {         User user = new User();         user.setName("Examination Proper noun");          when(userRepository.save(ArgumentMatchers.whatever(User.grade))).thenReturn(user);          User created = createUserService.createNewUser(user);          assertThat(created.getName()).isSameAs(user.getName());         verify(userRepository).save(user);     } }

If yous notice in the code, nosotros’re using the
assertThat
and
verify
methods to test different things.

By calling the
verify
method, nosotros’re checking that our repository was called and by calling
assertThat
we’re checking that our service answered our call with the right expected value.

Some notes most the annotations used :

  • @RunWith(MockitoJUnitRunner.class)
    : Invokes the class
    MockitoJUnitRunner
    to run the tests instead of running in the standard congenital in class.
  • @Mock
    : Used to simulate the behavior of a real object, in this case, our repository
  • @InjectMocks
    : Creates an case of the class and injects the mock created with the
    @Mock
    annotation into this instance
  • @Exam
    : Tells JUnit that the method to which this annotation is fastened tin can be run equally a test case

Our other endpoints will follow the same pattern, with the exception of those that depend upon an
id.

List all users service

src/test/java/com/usersapi/endpoints/unit/service/ListUserServiceTest.coffee

@RunWith(MockitoJUnitRunner.form) public class ListUserServiceTest {      @Mock     private UserRepository userRepository;      @InjectMocks     private ListUserService listUserService;      @Test     public void shouldReturnAllUsers() {         List<User> users = new ArrayList();         users.add(new User());          given(userRepository.findAll()).willReturn(users);          List<User> expected = listUserService.listAllUsers();          assertEquals(expected, users);         verify(userRepository).findAll();     } }

Delete an existing user service

For all endpoints that rely upon a given
id
similar this 1, we need to
throw an exception
for when the
id
doesn’t be. This exception needs to be likewise handled on unit of measurement tests.

src/exam/java/com/usersapi/endpoints/unit/service/DeleteUserServiceTest.java

@RunWith(MockitoJUnitRunner.course) public course DeleteUserServiceTest {      @Mock     private UserRepository userRepository;      @InjectMocks     private DeleteUserService deleteUserService;      @Test     public void whenGivenId_shouldDeleteUser_ifFound(){         User user = new User();         user.setName("Test Name");         user.setId(1L);          when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));          deleteUserService.deleteUser(user.getId());         verify(userRepository).deleteById(user.getId());     }      @Exam(expected = RuntimeException.form)     public void should_throw_exception_when_user_doesnt_exist() {         User user = new User();         user.setId(89L);         user.setName("Examination Name");          given(userRepository.findById(anyLong())).willReturn(Optional.ofNullable(aught));         deleteUserService.deleteUser(user.getId());     } }
            

Update an existing user service

southwardrc/test/java/com/usersapi/endpoints/unit of measurement/service/UpdateUserServiceTest.java

@RunWith(MockitoJUnitRunner.class) public form UpdateUserServiceTest {      @Mock     individual UserRepository userRepository;      @InjectMocks     private UpdateUserService updateUserService;      @Test     public void whenGivenId_shouldUpdateUser_ifFound() {         User user = new User();         user.setId(89L);         user.setName("Exam Name");          User newUser = new User();         user.setName("New Test Name");          given(userRepository.findById(user.getId())).willReturn(Optional.of(user));         updateUserService.updateUser(user.getId(), newUser);          verify(userRepository).salve(newUser);         verify(userRepository).findById(user.getId());     }      @Test(expected = RuntimeException.class)     public void should_throw_exception_when_user_doesnt_exist() {         User user = new User();         user.setId(89L);         user.setName("Examination Name");          User newUser = new User();         newUser.setId(90L);         user.setName("New Test Proper name");          given(userRepository.findById(anyLong())).willReturn(Optional.ofNullable(null));         updateUserService.updateUser(user.getId(), newUser);     } }

List an existing user service

src/test/java/com/usersapi/endpoints/unit/service/DetailUserServiceTest.java

@RunWith(MockitoJUnitRunner.grade) public class DetailUserServiceTest {      @Mock     private UserRepository userRepository;      @InjectMocks     individual DetailUserService detailUserService;      @Test     public void whenGivenId_shouldReturnUser_ifFound() {         User user = new User();         user.setId(89L);          when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));          User expected = detailUserService.listUser(user.getId());          assertThat(expected).isSameAs(user);         verify(userRepository).findById(user.getId());     }      @Test(expected = UserNotFoundException.class)     public void should_throw_exception_when_user_doesnt_exist() {         User user = new User();         user.setId(89L);         user.setName("Test Name");          given(userRepository.findById(anyLong())).willReturn(Optional.ofNullable(null));         detailUserService.listUser(user.getId());     } }

Testing the Controller layer

Our unit tests regarding our controllers should consist of a request and a verifiable response that nosotros’ll need to bank check if its what we expected or not.

By using the
MockMvcRequestBuilders
course, we can build a request and then laissez passer it as a parameter to the method which executes the actual asking. We so utilize the
MockMvc
form as the main entry point of our tests, executing requests by calling its perform method.

Lastly, nosotros can write assertions for the received response past using the static methods of the
MockMvcResultMatchers
form.

For a quick epitomize of our Controller layer lawmaking that volition exist tested, these are all our endpoints controller classes pasted into one section of code :

@RestController @RequestMapping("/users") public course CreateUserController {      @Autowired     CreateUserService service;      @PostMapping     @ResponseStatus(HttpStatus.CREATED)     public ResponseEntity<User> createNewUser_whenPostUser(@RequestBody User user) {          User createdUser = service.createNewUser(user);          URI uri = ServletUriComponentsBuilder.fromCurrentRequest()                 .path("/{id}")                 .buildAndExpand(createdUser.getId())                 .toUri();          render ResponseEntity.created(uri).body(createdUser);     } }  @RestController @RequestMapping("/users/{id}") public course DeleteUserController {      @Autowired     DeleteUserService service;      @DeleteMapping     @ResponseStatus(HttpStatus.NO_CONTENT)     public void deleteUser_whenDeleteUser(@PathVariable Long id) {         service.deleteUser(id);     } }  @RestController @RequestMapping("/users/{id}") public class DetailUserController {      @Autowired     DetailUserService service;      @GetMapping     @ResponseStatus(HttpStatus.OK)     public ResponseEntity<User> list(@PathVariable Long id) {         return ResponseEntity.ok().torso(service.listUser(id));     } }  @RestController @RequestMapping("/users") public class ListUserController {     @Autowired     ListUserService service;      @GetMapping     @ResponseStatus(HttpStatus.OK)     public ResponseEntity<Listing<User>> listAllUsers_whenGetUsers() {         return ResponseEntity.ok().body(service.listAllUsers());     } }  @RestController @RequestMapping("/users/{id}") public grade UpdateUserController {      @Autowired     UpdateUserService service;      @PutMapping     @ResponseStatus(HttpStatus.OK)     public ResponseEntity<User> updateUser_whenPutUser(@RequestBody User user, @PathVariable Long id) {         return ResponseEntity.ok().body(service.updateUser(id, user));     } }
            

Create a new user controller

Applying these steps in our
CreateUserControllerTest
nosotros should accept :

src/test/java/com/usersapi/endpoints/unit/controller/CreateUserControllerTest.coffee

@RunWith(SpringRunner.form) @WebMvcTest(CreateUserController.class) public class CreateUserControllerTest {     @Autowired     individual MockMvc mockMvc;      @MockBean     private CreateUserService service;      @Test     public void createUser_whenPostMethod() throws Exception {          User user = new User();         user.setName("Test Name");          given(service.createNewUser(user)).willReturn(user);          mockMvc.perform(post("/users")                 .contentType(MediaType.APPLICATION_JSON)                 .content(JsonUtil.toJson(user)))                 .andExpect(status().isCreated())                 .andExpect(jsonPath("$.name", is(user.getName())));     } }

About the
@MockBean
annotation, information technology’s from Leap Kick and is being used in our service, besides as beingness registered in our application context to verify the behavior of the mocked class.

Our
JsonUtil
class is being used to map and generate a
JSON
request for our “Create a new user” endpoint :

src/exam/java/com/usersapi/endpoints/util/JsonUtil.java

public course JsonUtil {     public static byte[] toJson(Object object) throws IOException {         ObjectMapper mapper = new ObjectMapper();         mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);         return mapper.writeValueAsBytes(object);     } }
            

Listing all users controller

src/examination/java/com/usersapi/endpoints/unit/controller/ListUserControllerTest.java

@RunWith(SpringRunner.course) @WebMvcTest(ListUserController.class) public course ListUserControllerTest {      @Autowired     private MockMvc mvc;      @MockBean     private ListUserService listUserService;      @Test     public void listAllUsers_whenGetMethod()             throws Exception {          User user = new User();         user.setName("Test proper name");          List<User> allUsers = Arrays.asList(user);          given(listUserService                 .listAllUsers())                 .willReturn(allUsers);          mvc.perform(go("/users")                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(status().isOk())                 .andExpect(jsonPath("$", hasSize(one)))                 .andExpect(jsonPath("$[0].proper name", is(user.getName())));     } }

Delete an existing user controller

As done with our service layer unit tests, we’ll demand to treat all endpoints that depend upon an
id
with additional configuration for a possible
RunTimeException
(UserNotFound) error that we specified previously :

src/test/java/com/usersapi/endpoints/unit/controller/DeleteUserControllerTest.coffee

@RunWith(SpringRunner.class) @WebMvcTest(DeleteUserController.form) public grade DeleteUserControllerTest {      @Autowired     private MockMvc mvc;      @MockBean     private DeleteUserService deleteUserService;      @Exam     public void removeUserById_whenDeleteMethod() throws Exception {         User user = new User();         user.setName("Test Proper name");         user.setId(89L);          doNothing().when(deleteUserService).deleteUser(user.getId());          mvc.perform(delete("/users/" + user.getId().toString())                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(condition().isNoContent());     }      @Examination     public void should_throw_exception_when_user_doesnt_exist() throws Exception {         User user = new User();         user.setId(89L);         user.setName("Test Name");          Mockito.doThrow(new UserNotFoundException(user.getId())).when(deleteUserService).deleteUser(user.getId());          mvc.perform(delete("/users/" + user.getId().toString())                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(status().isNotFound());      } }

Listing an existing user controller

src/test/java/com/usersapi/endpoints/unit/controller/DetailUserControllerTest.java

@RunWith(SpringRunner.class) @WebMvcTest(DetailUserController.class) public form DetailUserControllerTest {      @Autowired     private MockMvc mvc;      @MockBean     individual DetailUserService detailUserService;      @Exam     public void listUserById_whenGetMethod() throws Exception {          User user = new User();         user.setName("Examination Name");         user.setId(89L);          given(detailUserService.listUser(user.getId())).willReturn(user);          mvc.perform(get("/users/" + user.getId().toString())                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(condition().isOk())                 .andExpect(jsonPath("proper name", is(user.getName())));     }      @Test     public void should_throw_exception_when_user_doesnt_exist() throws Exception {         User user = new User();         user.setId(89L);         user.setName("Test Proper name");          Mockito.doThrow(new UserNotFoundException(user.getId())).when(detailUserService).listUser(user.getId());          mvc.perform(get("/users/" + user.getId().toString())                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(condition().isNotFound());     } }

Update an existing user service

This endpoint differs from others considering it needs a message body to be sent along with the request. For this nosotros’ll be using the
ObjectMapper
class to write into the content.

src/test/coffee/com/usersapi/endpoints/unit/controller/UpdateUserControllerTest.java

@RunWith(SpringRunner.course) @WebMvcTest(UpdateUserController.class) public class UpdateUserControllerTest {     @Autowired     private MockMvc mvc;      @MockBean     private UpdateUserService updateUserService;      @Test     public void updateUser_whenPutUser() throws Exception {          User user = new User();         user.setName("Exam Name");         user.setId(89L);         given(updateUserService.updateUser(user.getId(), user)).willReturn(user);          ObjectMapper mapper = new ObjectMapper();          mvc.perform(put("/users/" + user.getId().toString())                 .content(mapper.writeValueAsString(user))                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(status().isOk())                 .andExpect(jsonPath("name", is(user.getName())));     }      @Examination     public void should_throw_exception_when_user_doesnt_exist() throws Exception {         User user = new User();         user.setId(89L);         user.setName("Test Name");          Mockito.doThrow(new UserNotFoundException(user.getId())).when(updateUserService).updateUser(user.getId(), user);         ObjectMapper mapper = new ObjectMapper();          mvc.perform(put("/users/" + user.getId().toString())                 .content(mapper.writeValueAsString(user))                 .contentType(MediaType.APPLICATION_JSON))                 .andExpect(status().isNotFound());     } }

Endnotes

Through testing we larn well-nigh our awarding and make sure its code is rubber and reliable.

Next, let’due south add some integration tests, since we besides want to test the integration, and non just mock their behavior.

We hope this article was useful for yous, if you’d similar to enquire whatever question almost it, make sure to contact u.s.a..

Source code

You tin observe all the source code from this article bachelor in our github page hither.

Thanks for reading !! Experience gratuitous to leave whatever annotate.

Source: https://codefiction.net/unit-testing-crud-endpoints-of-a-spring-boot-java-web-service-api/