Unit Test in Spring-boot Application

By keylearn

Unit testing is an important aspect of software development, and Spring Boot provides a comprehensive testing framework that allows developers to write unit tests for their applications.

To write a unit test for a Spring Boot application, developers can use the Spring Test framework, which provides a set of annotations and utilities that make it easy to write tests.

Within this story , you’ll find a comprehensive overview of Spring Boot JUnit and how it work.

You can find the demo application in spring-boot-testing module in spring-boot-samples multi module project.

@SpringBootTest — used to load the application context and configure the environment.

@SpringBootTest
public class TestSpringBootApplication {

  @Autowired
  private ProductController controller;

  @Test
  public void contextLoads() throws Exception {
    assertThat(controller).isNotNull();
  }
}

Controller layer Unit Test

@ExtendWith(SpringExtension.class)@ExtendWith annotation is used to register extensions for JUnit5 test, SpringExtension.class allows the test to load the Spring context and inject dependencies into the test class.

@WebMvcTest — used for testing the web layer of a Spring MVC application. This annotation is used to test the web layer in isolation, without loading the entire application context. This annotaion required when you test the controller class. Further, The MockMvc instance is injected using @Autowired, which is used to perform HTTP requests and assertions.

When you have a dependency in controller @MockBean will help you to create and inject service dependancy in to the controller test class.

@ExtendWith(SpringExtension.class)
@WebMvcTest(ProductController.class)
public class ProductControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private ProductService productService;

  private Product product;
  private String requestPayload;

  @BeforeEach
  void setUp(){
    product = new Product(1, "test");
    requestPayload = "{\n"
        + "    \"productId\": 1,\n"
        + "    \"name\": \"test\"\n"
        + "}";
  }


  @Test
  public void getProductTest() throws Exception {

    when(this.productService.getProduct(any(Integer.class))).thenReturn(product);

    this.mockMvc.perform(get("/product/{productId}", "1"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("name").value("test"));
  }
}

Service layer unit test

@ExtendWith(MockitoExtension.class) is required when run unit test for service class. It tells JUnit 5 to load the Mockito framework and enable mocking of dependencies in the test. You can include SpringExtension.class insead of MockitoExtension.class. But since this is service layer, you don’t need to load spring context and it will add a extra delay as it loading the context. If there are any dependencies in service layer, @Mock annotation is used to create a mock object of the dependency.

@ExtendWith(MockitoExtension.class)
public class ProductServiceTest {

  @Mock
  private ProductRepository productRepository;

  private ProductService productService;

  private Product responsePayload;


  @BeforeEach
  void setUp(){
    productService = new ProductServiceImpl(productRepository);
    responsePayload = new Product(1, "test");

  }

  @Test
  public void getProductTest(){

    when(productRepository.findById(any(Integer.class))).thenReturn(
        Optional.of(responsePayload));

    Product result = productService.getProduct(1);

    assertThat(result.getName()).isEqualTo(responsePayload.getName());
  }
}

Repository layer unit test

When a test class is annotated with @DataJpaTest, it configures an in-memory H2 database and loads only the necessary Spring beans required for testing the persistence layer. These beans include the EntityManager, DataSource, EntityManagerFactory, and JpaRepository beans. Also need to add @ExtendWith(SpringExtension.class) to load the spring context. @AutoConfigureTestDatabase annotation is used to configure the test database used by the test class. The Replace.NONE option is used to specify that the default test database should not be replaced. The TestEntityManager is injected using @Autowired, which is used to interact with the in-memory database.

@ExtendWith(SpringExtension.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class ProductRepositoryTest {

  @Autowired
  private ProductRepository productRepository;

  @Autowired
  private TestEntityManager testEntityManager;

  @Test
  public void findByIdTest(){
    Product savedProduct = testEntityManager.persistAndFlush(new Product(1,"test"));

    Optional<Product> product = this.productRepository.findById(1);

    assertThat(product.get().getName()).isEqualTo(savedProduct.getName());
  }
}

Repository Link — spring-boot-testing

That is the end of this story. Thank you!

Share: