Creating a REST API using Spring Boot + Tests + Documentation [part 04]
Tags: webdev, junit, testing, java
Published: 2023-02-01 18:16:33

Hello!

I'm back in this tutorial to teach you how to develop a REST API using Spring Boot. As I said earlier, this tutorial is divided into 5 parts as shown in the following table:


|   **Part**  |      **Content**      |
|:-----------:|:---------------------:|
|   Part 01   |  [Application Startup](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-01-2262)  |
|   Part 02   | [Settings and Database](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-02-1a4i) |
|   Part 03   |   [API Implementation](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-03-1m5e)  |
| **Part 04** |  **Tests + Coverage** |
|   Part 05   | [Swagger Documentation](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-05-1pd6) |

--- 

It is expected that from this fourth part you will learn the following:

- Use of Junit
- Creation of Unit Tests
- Creation of Integration Tests
- Using Fakers to create test data

Remember that in this tutorial the following tools are used:

- JDK 17
- IntelliJ
- Maven
- Insomnia
- MySQL database


---

## **Step 01 - Creating test properties**

Usually when we have to test our application, we create a properties file dedicated to the tests. This is done to have a better organization of development and test environments.
So, inside `resources` you can create a new file called `application-test.properties` which will contain the following content:

```

spring.datasource.url=jdbc:mysql://localhost:3307/small_library_test
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

```
The first three items are responsible of the database connection. Note that a new database (`small_library_test`) has been created for the tests but if you want, you can use the same database used for development.
The last item is present to avoid the `LazyInitializationException` exception.

Therefore, when we create our tests, they will be using the properties defined in this new file.

---


## **Step 02 - Exception Handlers**


By default, Spring handles some exceptions through classes, such as `EntityNotFoundException` and `EmptyResultDataAccessException`.

The first case happens when the user tries to search for an object in the database that does not exist, for example they would like to find a book whose id is equal to 9999999999 (thinking about the case in which this id does not exist in the database). By default, it returns error 500 (bad request) along with the exception body.

But let's think that in this case instead of returning the error 500, we would like to return the error 404 (Not Found). How can we carry out this modification?

That's why there is the `RestControllerAdvice`, it is used for handling exceptions in controllers. For its creation, you'll have to go to the `src.main.java.com.simplelibrary.simplelibrary.API` package and create a new package called `exception`. And inside this, create a class called `ErrorHandler`:

```

package com.simplelibrary.simplelibraryAPI.exception;

import jakarta.persistence.EntityNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.simplelibrary.simplelibraryAPI.dto.ErrorValidatorDTO;

@RestControllerAdvice
public class ErrorHandler {
    @ExceptionHandler({EntityNotFoundException.class, EmptyResultDataAccessException.class})
    public ResponseEntity error404(){
        return ResponseEntity.notFound().build();
    }


    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity error400(MethodArgumentNotValidException exception){
        var erros = exception.getFieldErrors();
        return ResponseEntity.badRequest().body(erros.stream().map(ErrorValidatorDTO::new).toList());
    }



}

```
In order for Spring to recognize that class as a Rest Controller Advice, you must place the `@RestControllerAdvice` annotation before the class initialization.

For each exception you want to handle, you must put it inside the `@ExceptionHandler` annotation saying that that exception will be handled inside the next method.
Note that the `error404` method will handle exceptions that normally throw the 500 error and cause them to throw the 404 error.

The `error400` method will handle the exception MethodArgumentNotValidException. This exception is triggered when there is a validation error, for example, when the user tries to register a book without its title. Originally, the response he will receive is as follows:

```

{
	"timestamp": "2023-01-27T19:04:03.372+00:00",
	"status": 400,
	"error": "Bad Request",
	"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity com.simplelibrary.simplelibraryAPI.controller.BookController.store(com.simplelibrary.simplelibraryAPI.dto.BookRequestDTO,org.springframework.web.util.UriComponentsBuilder) throws java.io.IOException: [Field error in object 'bookRequestDTO' on field 'title': rejected value [null]; codes [NotBlank.bookRequestDTO.title,NotBlank.title,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [bookRequestDTO.title,title]; arguments []; default message [title]]; default message [não deve estar em branco]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:144)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:181)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:148)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1010)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:913)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:884)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:833)\r\n",
	"message": "Validation failed for object='bookRequestDTO'. Error count: 1",
	"errors": [
		{
			"codes": [
				"NotBlank.bookRequestDTO.title",
				"NotBlank.title",
				"NotBlank.java.lang.String",
				"NotBlank"
			],
			"arguments": [
				{
					"codes": [
						"bookRequestDTO.title",
						"title"
					],
					"arguments": null,
					"defaultMessage": "title",
					"code": "title"
				}
			],
			"defaultMessage": "must not be blank",
			"objectName": "bookRequestDTO",
			"field": "title",
			"rejectedValue": null,
			"bindingFailure": false,
			"code": "NotBlank"
		}
	],
	"path": "/books"
}

```
Realize the amount of information that is sent to the user, when in fact we just want them to know which field is with the  error and what the error message is. Therefore, we will carry out your treatment through a DTO.

Inside the `src.main.java.com.simplelibrary.simplelibrary.API.dto` package we will create a record called `ErrorValidatorDTO`:

```

package com.simplelibrary.simplelibraryAPI.dto;
import org.springframework.validation.FieldError;
public record ErrorValidatorDTO(String field, String message){
    public ErrorValidatorDTO(FieldError error){
        this(error.getField(), error.getDefaultMessage());
    }
}

```
From there we will get the error object and return to the user only the necessary information:

```

[
	{
		"field": "title",
		"message": "must not be blank"
	}
]


```

---

## **Step 03 - Book Factory**

For our tests it will be necessary to create several books with different information. Carrying out this manual work is a bit exhausting, so we can use mechanisms created by the community.

One of the mechanisms we will use is `Faker`. From it, we will be able to generate random values for the fields and create our book object. For that, you'll need to import the following dependency into your `pom.xml` file:

```

<dependency>
   <groupId>com.github.javafaker</groupId>
   <artifactId>javafaker</artifactId>
   <version>1.0.2</version>
</dependency>

```

By doing this we can create the `BookFactory` class that will be responsible for creating book objects (DAO and DTO request models):

Inside the `src.main.java.com.simplelibrary.simplelibrary.API.factory` package we will create a class called `BookFakerFactory`:

```

package com.simplelibrary.simplelibraryAPI.factory;

import com.github.javafaker.Faker;
import com.simplelibrary.simplelibraryAPI.dto.BookRequestDTO;
import com.simplelibrary.simplelibraryAPI.model.Book;

public class BookFakerFactory {

    private Faker faker;

    public BookFakerFactory(){
        this.faker = new Faker();
    }

    public BookRequestDTO createBook(){
        var book = new BookRequestDTO(faker.book().title(), faker.book().author(),
                faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
                faker.random().nextBoolean());

        return book;
    }

    public BookRequestDTO createBook(Book bookCreated){
        var book = new BookRequestDTO(bookCreated);
        return book;
    }

    public Book createBookModel(){
        var bookDTO = new BookRequestDTO(faker.book().title(), faker.book().author(),
                faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
                faker.random().nextBoolean());

        var book = new Book(bookDTO);
        return book;
    }



    public BookRequestDTO createBookIncomplete(){
        var book = new BookRequestDTO(faker.book().title(), "",
                faker.number().randomDigit(), faker.number().digits(13), faker.number().randomDouble(1,1,10),
                faker.random().nextBoolean());

        return book;
    }

}


```
Note that we have 4 methods and the objective of all is to create an object that will later be used in tests.

- The `createBook` method will create a `BookRequestDTO` object that will be used to test requests.
- The `createBook` method (with parameter) will create a `BookRequestDTO` object from a previously created book.
- The `createBookModel` method will create a `Book` object.
- The `createBookIncomplete` method will create a `BookRequestDTO` object without author information.

Note that all methods use the `Faker` library.

---

## **Step 04 - Unit Tests**

We are ready to create our tests. If you look inside the `test` package, there is already a class called `SimplelibraryApiApplicationTests`:

```

package com.simplelibrary.simplelibraryAPI;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SimplelibraryApiApplicationTests {


	@Test
	void contextLoads() throws Exception {


	}

}


```

It will use `JUnit` for testing.

Note some annotations that are important:

- `@SpringBootTest` indicates that that class will contain methods that should be tested.
- `@Test` indicates that that method should be run as a test.

So we can create our first unit test. This one will test some features of our repository: inside the `src.main.test.java.com.simplelibrary.simplelibrary.API` package we will create a package called `unit` and inside it a class called `BookTest`:

```

package com.simplelibrary.simplelibraryAPI.unit;

import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;

@SpringBootTest
@ActiveProfiles("test")
public class BookTest {

    @Autowired
    private BookRepository repository;

    @Autowired
    private BookService service;

    private BookFakerFactory bookFakerFactory;

    public BookTest() {
        this.bookFakerFactory = new BookFakerFactory();
    }

   

}

```

Notice the `@ActiveProfiles("test")` annotation. It is used to inform Spring that we are using the properties defined inside the `application-test.properties` file.

We can think of some tests to be done:

- Test for successfully creating a book.

- Test to verify if the service of collecting the rating of a given book is working correctly (verifying that the rating value is populated when it is from an existing book and that it is not populated with a non-existing book).

The class with all the tests looks like this:

```

package com.simplelibrary.simplelibraryAPI.unit;

import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import com.simplelibrary.simplelibraryAPI.service.BookService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;

@SpringBootTest
@ActiveProfiles("test")
public class BookTest {

    @Autowired
    private BookRepository repository;

    @Autowired
    private BookService service;

    private BookFakerFactory bookFakerFactory;

    public BookTest() {
        this.bookFakerFactory = new BookFakerFactory();
    }

    @Test
    public void itShouldCreateABookSuccessfully(){
        var book = bookFakerFactory.createBookModel();
        repository.save(book);

        var bookFinder = repository.findBookByisbn(book.getIsbn());

        Assertions.assertThat(book).isEqualTo(bookFinder);
    }

    @Test
    public void itShouldFillTheRatingValueFromExistingBook() throws IOException {
        var book = bookFakerFactory.createBookModel();
        book.setTitle("Harry Potter and the Philosopher's Stone");

        var bookCreated = service.store(bookFakerFactory.createBook(book));

        assertThat(bookCreated.getRate()).isNotNull()
                .isNotZero()
                .isGreaterThan(Double.valueOf(0));

    }

    @Test
    public void itShouldNotFillTheRatingValueFromNonexistentBook() throws IOException {
        var book = bookFakerFactory.createBookModel();
        book.setTitle("blablablablablabla");

        var bookCreated = service.store(bookFakerFactory.createBook(book));

        assertThat(bookCreated.getRate()).isZero();

    }



}


```

Note the use of the `assertThat` method present in JUnit, which allows checking values.

---

## **Step 05 - Integration tests**

We can now create the integration test that will allow us to test our system's routes. We will also check if the requests are returned correctly and if the http codes also match expectations. 

Inside the `src.main.test.java.com.simplelibrary.simplelibrary.API` package we will create a package called `integration` and inside it a class called `BookControllerTest`:

```

package com.simplelibrary.simplelibraryAPI.integration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class BookControllerTest {

@Autowired
    private MockMvc mockMvc;

    @Autowired
    private BookRepository repository;

    private BookFakerFactory bookFakerFactory;

    public BookControllerTest() {
        this.bookFakerFactory = new BookFakerFactory();
    }


} 

```

Note the use of a new annotation: `@AutoConfigureMockMvc` from it we will be able to use the injected `MockMvc` class to make requests to our server.

We can think of the following test cases:

- Verification if a book is successfully created
- Verification if a book is not created when we don't send all the necessary fields (http code 400)
- Verification if all books are successfully listed
- Verification if the information of a single book appears successfully.
- Verification if a book's information is changed successfully
- Verification that a book's information is not successfully changed when its information is not submitted.
- Verification if a book is successfully deleted.
- Verification that a non-existent book is not deleted.

The class with all implemented methods looks like this:

```

package com.simplelibrary.simplelibraryAPI.integration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.simplelibrary.simplelibraryAPI.factory.BookFakerFactory;
import com.simplelibrary.simplelibraryAPI.repository.BookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private BookRepository repository;

    private BookFakerFactory bookFakerFactory;

    public BookControllerTest() {
        this.bookFakerFactory = new BookFakerFactory();
    }

    @Test
    void testIfBookIsCreatedSuccessfully() throws Exception {
        var book = this.bookFakerFactory.createBook();

        var result = this.mockMvc.perform(post("/books")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(asJsonString(book)))
                .andExpect(status().isCreated())
                .andReturn();

        String content = result.getResponse().getContentAsString();
        assertNotNull(content);

    }

    @Test
    void testIfBookIsNotCreatedWithMissingAttributes() throws Exception{
        var book = this.bookFakerFactory.createBookIncomplete();
        this.mockMvc.perform(post("/books")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(asJsonString(book)))
                .andExpect(status().isBadRequest())
                .andReturn();

    }

    @Test
    void testIfAllBooksAreListedSuccessfully() throws Exception{
        this.mockMvc.perform(get("/books"))
                .andExpect(status().isOk())
                .andReturn();
    }

    @Test
    void testIfSingleBookInformationIsShown() throws  Exception {
        this.mockMvc.perform(get("/books"))
                .andExpect(status().isOk())
                .andReturn();
    }

    @Test
    void testIfBookIsUpdatedSuccessfully() throws Exception{
        var book = this.bookFakerFactory.createBookModel();
        var bookCreated = repository.save(book);

        book.setAuthor("New Author name");

        var book2 = this.bookFakerFactory.createBook(book);

        this.mockMvc.perform(put("/books/{id}",bookCreated.getId())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(asJsonString(book2)))
                .andExpect(status().isOk())
                .andReturn();

        var bookUpdated = repository.getReferenceById(bookCreated.getId());

        assertThat(bookUpdated.getId()).isEqualTo(bookCreated.getId());
        assertThat(bookUpdated.getAuthor()).isNotEqualTo(bookCreated.getAuthor());


    }

    @Test
    void testIfBookIsNotUpdatedSuccessfully() throws Exception{
        var book = this.bookFakerFactory.createBookModel();
        var bookCreated = repository.save(book);


        this.mockMvc.perform(put("/books/{id}",bookCreated.getId()))
                .andExpect(status().isBadRequest())
                .andReturn();


    }

    @Test
    void testIfBookIsDeletedSuccessfully() throws Exception{
        var book = this.bookFakerFactory.createBookModel();
        var bookCreated = repository.save(book);
        this.mockMvc.perform(delete("/books/"+bookCreated.getId()))
                .andExpect(status().isNoContent())
                .andReturn();
    }

    @Test
    void testIfBookIsNotDeletedSuccessfully() throws Exception{
        this.mockMvc.perform(delete("/books/999999"))
                .andExpect(status().isNotFound())
                .andReturn();

    }

    public static String asJsonString(final Object obj) {
        try {
            return new ObjectMapper().writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }
}


```

Note the use of the `asJsonString` method. It is used to take all the attribute values of an object and transform it into a Json string.

---

## **Step 06 - Running the tests**

To run the tests you will have to go to the terminal and type the following command:

```

\.mvnw test

```

By doing this you will inform maven that you want to run all the tests in your application:

![Tests being executed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mholdx2yr2rcjifmujlt.png)

If all tests run successfully, you will see the following:


![Tests run successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4umjyqfmao0c7qbm9oxa.png)

---

## **Step 07 - Test Coverage**

Finally, many times when we do tests we would like to understand how broad it was. We would like to reach as many classes and methods as possible in our project, thus avoiding letting any part go unnoticed.

We can use the **JaCoCo** plugin to check test coverage. Its documentation is present [at this link](https://www.jacoco.org/jacoco/).

To use it, you'll need to go to your `pom.xml` file and add the following `plugin`:

```

<plugin>
   <groupId>org.jacoco</groupId>
   <artifactId>jacoco-maven-plugin</artifactId>
   <version>0.8.8</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>


```

Just that! Now you can rerun your tests and when they are finished a `.html` file will be generated inside `target.site.jacoco`. The file is `index.html` and when accessing it you will have a screen similar to this one:


![JaCoCo's initial screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ycan806qud8dqr75uxtk.png)

As you can see, the `com.simplelibrary.simplelibraryAPI.model` package had 70% of coverage. For more information, you can click on the package and then on the desired class to have the following coverage view:


![Book coverage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rfq1wjrpyj4cff7e4d2.png)

- Green means that the code snippet was called.

Therefore, you can use the plugin and add new tests so that the coverage value of a given class is as high as possible!

---
## **What we learned in this tutorial**

In this third part, we learned how to perform unit and integration tests using JUnit. We also saw how we can check test coverage using the JaCoCo plugin.

In the [next part](https://dev.to/franciscotis/creating-a-rest-api-using-spring-boot-tests-documentation-part-05-1pd6) you will see about project documentation using Swagger.

Remembering that the complete project can be found [HERE](https://github.com/franciscotis/simple-library-api)