본문 바로가기

카테고리 없음

[테스트 코드]JUnit

1. 목적

협업을 하면서 커밋을 기다리거나 구현한 기능에 대해 실행하면서 웹페이지로 테스트를 진행하는 것이 비효율적임을 느끼게 되어 테스트코드를 이용한 단위테스트를 필수적으로 진행해야겠다고 생각했다. 

2. 학습내용

given-when-then

  • given은 테스트 실행을 준비하는 단계
  • when은 테스트를 진행하는 단계
  • then은 테스트 결과를 검증하는 단계

JUnit 애너테이션

  • @DisplayName : 테스트 이름을 명시
  • @Test : 테스트 수행 메서드
  • @BeforeAll : 전체테스트를 시작하기 전에 처음으로 한번만 실행한다. 데이터베이스를 연결해야 하거나 테스트 환경을 초기화할 때 사용된다. 전체 테스트 실행 주기에서 한번만 호출되어야 하기 때문에 statc으로 선언해야 한다.
  • @BeforeEach : 테스트 케이스를 시작하기전에 매번 실행한다. 테스트 메서드에서 사용하는 객체를 초기화하거나 테스트에 필요한 값을 미리 넣을 때 사용한 수 있다. 각 인스턴스에 대해 메서드를 호출해야 하므로 메서드는 static이 아니어야 한다.
  • @AfterAll : 전체 테스트를 마치고 종료하기 전에 한 번만 실행한다. 데이터베이스 연결을 종료할때나 공통적으로 사용한 자원을 해제할 때 사용할 수 있다. 전체 테스트 실행 주기에서 한번만 호출되어야 하므로 메서드를 static으로 선언해야 한다.
  • @AfterEach : 각 테스트 케이스를 종료하기 전 매번 실행한다. 테스트 이후에 특정 데이터를 삭제해야 하는 경우 사용한다. static이 아니어야 한다.

AssertJ

Assertion.assertEquals(a+b, sum) -> assertThat(a+b).isEqualTo(sum);

 

3. 작성코드

package com.team.RecipeRadar.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.team.RecipeRadar.domain.recipe.domain.Recipe;
import com.team.RecipeRadar.domain.recipe.dto.AddRecipeRequest;
import com.team.RecipeRadar.domain.recipe.dto.UpdateRecipeRequest;
import com.team.RecipeRadar.domain.recipe.dao.RecipeRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class RecipeControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    @Autowired // 직렬화, 역직렬화를 위한 클래스
    protected ObjectMapper objectMapper;

    @Autowired
    private WebApplicationContext context;

    @Autowired
    RecipeRepository recipeRepository;

    @BeforeEach // 테스트 실행전 실행하는 메서드
    public void setMockMvc() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(context).
                build();
        recipeRepository.deleteAll();

    }

    @DisplayName("addRecipe: 레시피 추가에 성공한다.")
    @Test
    public void addRecipe() throws Exception {
        //given
        final String url = "/api/admin/recipes";
        final String recipeTitle = "recipeTitle";
        final String recipeContent = "recipeContent";
        final String recipeServing = "recipeServing";
        final String cookingTime = "cookingTime";
        final String ingredientsAmount = "ingredientsAmount";
        final String cookingStep = "cookingStep";
        final String recipeLevel = "recipeLevel";
        final AddRecipeRequest userRequest = new AddRecipeRequest(recipeTitle, recipeContent, recipeServing, cookingTime, ingredientsAmount, cookingStep, recipeLevel);

        //객체 JSON으로 직렬화
        final String requestBody = objectMapper.writeValueAsString(userRequest);

        //when
        //설정한 내용을 바탕으로 요청 전송
        ResultActions result = mockMvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(requestBody));

        //then
        result.andExpect(status().isCreated());

        List<Recipe> recipes = recipeRepository.findAll();

        assertThat(recipes.size()).isEqualTo(1); //크기가 1인지 검증
        assertThat(recipes.get(0).getRecipeTitle()).isEqualTo(recipeTitle);
        assertThat(recipes.get(0).getRecipeContent()).isEqualTo(recipeContent);
        assertThat(recipes.get(0).getRecipeServing()).isEqualTo(recipeServing);
        assertThat(recipes.get(0).getCookingTime()).isEqualTo(cookingTime);
        assertThat(recipes.get(0).getIngredientsAmount()).isEqualTo(ingredientsAmount);
        assertThat(recipes.get(0).getCookingStep()).isEqualTo(cookingStep);
        assertThat(recipes.get(0).getRecipeLevel()).isEqualTo(recipeLevel);
    }

    @DisplayName("findAllRecipes: 레시피 목록 조회에 성공한다.")
    @Test
    public void findAllRecipes() throws Exception {
        final String url = "/api/admin/recipes";
        final String recipeTitle = "recipeTitle";
        final String recipeContent = "recipeContent";
        final String recipeServing = "recipeServing";
        final String cookingTime = "cookingTime";
        final String ingredientsAmount = "ingredientsAmount";
        final String cookingStep = "cookingStep";
        final String recipeLevel = "recipeLevel";

        recipeRepository.save(Recipe.builder()
                .recipeTitle(recipeTitle)
                .recipeContent(recipeContent)
                .recipeServing(recipeServing)
                .cookingTime(cookingTime)
                .ingredientsAmount(ingredientsAmount)
                .cookingStep(cookingStep)
                .recipeLevel(recipeLevel)
                .build());

        final ResultActions resultActions = mockMvc.perform(get(url)
                .accept(MediaType.APPLICATION_JSON));

        resultActions
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].recipeLevel").value(recipeLevel))
                .andExpect(jsonPath("$[0].cookingStep").value(cookingStep))
                .andExpect(jsonPath("$[0].ingredientsAmount").value(ingredientsAmount))
                .andExpect(jsonPath("$[0].cookingTime").value(cookingTime))
                .andExpect(jsonPath("$[0].recipeServing").value(recipeServing))
                .andExpect(jsonPath("$[0].recipeContent").value(recipeContent))
                .andExpect(jsonPath("$[0].recipeTitle").value(recipeTitle));

    }

    @DisplayName("findRecipe: 레시피 상세 조회에 성공한다.")
    @Test
    public void findRecipe() throws Exception {
        final String url = "/api/admin/recipes/{id}";
        final String recipeTitle = "recipeTitle";
        final String recipeContent = "recipeContent";
        final String recipeServing = "recipeServing";
        final String cookingTime = "cookingTime";
        final String ingredientsAmount = "ingredientsAmount";
        final String cookingStep = "cookingStep";
        final String recipeLevel = "recipeLevel";

        Recipe savedRecipe = recipeRepository.save(Recipe.builder()
                .recipeTitle(recipeTitle)
                .recipeContent(recipeContent)
                .recipeServing(recipeServing)
                .cookingTime(cookingTime)
                .ingredientsAmount(ingredientsAmount)
                .cookingStep(cookingStep)
                .recipeLevel(recipeLevel)
                .build());

        final ResultActions resultActions = mockMvc.perform(get(url, savedRecipe.getId()));

        resultActions
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.recipeLevel").value(recipeLevel))
                .andExpect(jsonPath("$.cookingStep").value(cookingStep))
                .andExpect(jsonPath("$.ingredientsAmount").value(ingredientsAmount))
                .andExpect(jsonPath("$.cookingTime").value(cookingTime))
                .andExpect(jsonPath("$.recipeServing").value(recipeServing))
                .andExpect(jsonPath("$.recipeContent").value(recipeContent))
                .andExpect(jsonPath("$.recipeTitle").value(recipeTitle));
    }

    @DisplayName("deleteRecipe: 레시피 삭제에 성공한다.")
    @Test
    public void deleteRecipe() throws Exception {
        final String url = "/api/admin/recipes/{id}";
        final String recipeTitle = "recipeTitle";
        final String recipeContent = "recipeContent";
        final String recipeServing = "recipeServing";
        final String cookingTime = "cookingTime";
        final String ingredientsAmount = "ingredientsAmount";
        final String cookingStep = "cookingStep";
        final String recipeLevel = "recipeLevel";


        Recipe savedRecipe = recipeRepository.save(Recipe.builder()
                .recipeTitle(recipeTitle)
                .recipeContent(recipeContent)
                .recipeServing(recipeServing)
                .cookingTime(cookingTime)
                .ingredientsAmount(ingredientsAmount)
                .cookingStep(cookingStep)
                .recipeLevel(recipeLevel)
                .build());

        mockMvc.perform(delete(url, savedRecipe.getId()))
                .andExpect(status().isOk());

        List<Recipe> recipes = recipeRepository.findAll();

        assertThat(recipes).isEmpty();
    }

    @DisplayName("updateRecipe: 레시피 수정에 성공한다.")
    @Test
    public void updateRecipe() throws Exception {
        final String url = "/api/admin/recipes/{id}";
        final String recipeTitle = "recipeTitle";
        final String recipeContent = "recipeContent";
        final String recipeServing = "recipeServing";
        final String cookingTime = "cookingTime";
        final String ingredientsAmount = "ingredientsAmount";
        final String cookingStep = "cookingStep";
        final String recipeLevel = "recipeLevel";

        Recipe savedRecipe = recipeRepository.save(Recipe.builder()
                .recipeTitle(recipeTitle)
                .recipeContent(recipeContent)
                .recipeServing(recipeServing)
                .cookingTime(cookingTime)
                .ingredientsAmount(ingredientsAmount)
                .cookingStep(cookingStep)
                .recipeLevel(recipeLevel)
                .build());

        final String newRecipeTitle = "new recipeTitle";
        final String newRecipeContent = "new recipeContent";
        final String newRecipeServing = "new recipeServing";
        final String newCookingTime = "new cookingTime";
        final String newIngredientsAmount = "new ingredientsAmount";
        final String newCookingStep = "new cookingStep";
        final String newRecipeLevel = "new recipeLevel";

        UpdateRecipeRequest request = new UpdateRecipeRequest(newRecipeTitle, newRecipeContent, newRecipeServing, newCookingTime, newIngredientsAmount, newCookingStep, newRecipeLevel);

        ResultActions result = mockMvc.perform(put(url, savedRecipe.getId())
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(objectMapper.writeValueAsString(request)));

        result.andExpect(status().isOk());

        Recipe recipe = recipeRepository.findById(savedRecipe.getId()).get();

        assertThat(recipe.getRecipeTitle()).isEqualTo(newRecipeTitle);
        assertThat(recipe.getRecipeContent()).isEqualTo(newRecipeContent);
        assertThat(recipe.getRecipeServing()).isEqualTo(newRecipeServing);
        assertThat(recipe.getCookingTime()).isEqualTo(newCookingTime);
        assertThat(recipe.getIngredientsAmount()).isEqualTo(newIngredientsAmount);
        assertThat(recipe.getCookingStep()).isEqualTo(newCookingStep);
        assertThat(recipe.getRecipeLevel()).isEqualTo(newRecipeLevel);
    }


}

4. 마무리

service 패키지의 기능들과 예외처리관련해서 테스트가 어떻게 이루어지는지 공부해봐야겠다.