JPA 사용한 카테고리 구현 (infinite depth) - 04 TDD, 테스트 코드 만들기

Joshua_Kim·2021년 7월 28일
5
post-thumbnail
post-custom-banner

"민재씨 무한뎁스로 카테고리 로직 구현해봐요"

입사 후 공부하며 기술 스텍을 쌓던 내게 던져진 첫 실무 과제.

stack : Springboot 2.3.6 RELEASE, gradle, JPA, mysql ...


👉🏻 JPA 사용한 카테 고리 구현 1편 (요구조건, Entity, Repository) (링크 click)

👉🏻 JPA 사용한 카테 고리 구현 2편 (DTO, Service - save) (링크 click)

👉🏻 JPA 사용한 카테 고리 구현 3편 (Service - update, delete, get) (링크 click)

저번시간에 이어 이번에는 test 코드를 작성해보자.

🌱 TDD는 뭐고, TEST 코드란 뭘꼬?

  • 서비스가 커지고, 로직이 복잡해질 수록 TEST 코드의 정밀함은 매우 중요하다.

  • 나도 실무를 시작하면서 TDD의 중요성을 실감하고 있다.

TDD : Test-Driven Development 즉, 테스트 주도 개발을 의미 🤔

  • 실무는 더 이상 Hello world 만 출력하는 수준의 코딩이 아니다.
    Sub module, MSA 등의 개념들이 들어가고 코드량이 많아지면 분명 내가 만든 건데도 헷갈릴때가 많다.

  • 그렇기 때문에, 핵심 로직을 통과하기 위한 최소한의 데이터와 코드를 생성하여 테스트 하며 정교하게 비지니스 로직들을 디버깅하고, 문제를 발견하는 작업이 매우 중요하다.

  • 테스트 주도의 설계를 하면 핵심로직을 짜기 전, 미리 테스트를 해볼수 있고 설계를 수정 한 후에 핵심 로직 개발을 들어갈 수 있기 때문에 발생되는 문제와 설계 오류를 쉽게 잡을 수 있다.
    사실 아직 나도 TDD가 낯설다 현실은 개발하고 테스트하고 있다.

  • 엄밀히 말해, 지금 진행하고 있는 카테고리 구현에서 테스트 코드를 작성하는 시점이 TDD라고 말하기는 어렵다. 핵심 로직을 대충 거의 만들어 놓고, TEST를 구현하고 있기 때문인데, 이후 Refactor작업에서 핵심 비지니스로직을 완성할 것이기 때문에... 봐주세요

⚙️ 5 TEST 코드 구현

🛠 5-1 기본 테스트 코드 설정

  • 테스트 코드는 JUnit5로 진행하였다.

  • 테스트를 진행하기위해 필요한 의존성과, 반복되는 작업을 줄여주는 method 코드를 보자.

@SpringBootTest
@Transactional
class CategoryServiceTest {
    @Autowired
    CategoryService categoryService;
    @Autowired
    CategoryRepository categoryRepository;

    //SavedID
    private CategoryDTO createCategoryDTO(String testBranch, String testCode, String testName) {
        CategoryDTO categoryDTO = new CategoryDTO();
        categoryDTO.setBranch(testBranch);
        categoryDTO.setCode(testCode);
        categoryDTO.setName(testName);
        return categoryDTO;
    }

    //Find Category

    private Category findCategory (Long savedId) {
        return categoryRepository.findById(savedId).orElseThrow(IllegalArgumentException::new);
    }

🗝 key points

  • SpringBootTest 어노테이션은 Junit5 환경에서 테스트를 하기위해 필요한 필수 어노테이션이다.

  • @transactional 어노테이션은 테스트 코드내에서 각각의 테스트들이 하나의 transaction으로 관리 할 수 있도록 해준다. 즉, 하나의 테스트가 끝나면 테스트 코드들의 데이터들을 DB에서 rollback 해주기 때문에, 테스트가 온전히 그 자체만으로 데이터 훼손 없이 일어날 수 있게 된다.

  • @Autowired 어노테이션을 통해 Springbean으로 등록한 CategoryServiceCategoryRepository 를 주입 받는다.

  • createCategoryDTO() 는 밑에 테스트에서 반복되는 코드를 줄이기 위해 호출해서 간편히 쓰려구 만들었다.

  • findCategory()도 마찬가지.

  • 코드 간편화와 반복작업을 줄이는 것은 개발자의 숙명. 개발자는 게을러야해



✍🏻 5-2 카테고리-저장-테스트()

  • 나는 주로 테스트 코드의 메서드 이름을 한글로 적는 편이다.

  • 그게 가독성도 좋고, 어떤걸 테스트하고 있는지 확실히 알수 있어서 좋다.

@Test
    public void 카테고리_저장_테스트 () {
        //given
        CategoryDTO categoryDTO = createCategoryDTO("TestBranch", "TestCode", "TestName");
        Long savedId = categoryService.saveCategory(categoryDTO);

        //when
        Category category = findCategory(savedId);

        //then
        assertThat(category.getCode()).isEqualTo("TestCode");

    }

🗝 key points

  • 사실, 테스트코드는 너무 간단하다.

  • DTO를 만들고, 그 DTO 객체를 테스트할 categoryService.saveCategory() 메소드에 파라미터로 넘겨줘서 테스트해준다.

  • 🖍 assertThat 메소드는 테스트 코드의 핵심이다.

import static org.assertj.core.api.Assertions.*;

  • Junit5를 통해 생성된 테스트의 Assertionsorg.junit.jupiter.api.Assertions.*;static으로 import를 한다. 이것과 다른 assertjAssertions를 사용하여 테스트 했다.


✍🏻 5-3 카테고리-업데이트-테스트()

  • 업데이트 테스트를 위해서는 테스트 데이터를 테스트 DB에 저장을 해야하고, 그 저장한 데이터를 다시 꺼내와서 target 객체를 가지고 요리해줘야 한다.

  • 개발자는 코드를 봐야 이해가 빠르다. 코드를 보자.

@Test
    public void 카테고리_업데이트_테스트 () {
        //given
        CategoryDTO categoryDTO = createCategoryDTO("TestBranch", "TestCode", "TestName");
        Long savedId = categoryService.saveCategory(categoryDTO);

        Category category = findCategory(savedId);
        CategoryDTO targetCategory = new CategoryDTO(category);
        targetCategory.setName("UpdateCategory");
        
        //when

        Long updateId = categoryService.updateCategory("TestBranch", "TestCode", targetCategory);
        Category updatedCategory = findCategory(updateId);

        //then
        assertThat(updatedCategory.getName()).isEqualTo("UpdateCategory");
    }

🗝 key points

  • 업데이트도 매우 간단하다.

  • 가져온 targetCategory 객체를 setter를 통해 name을 바꿔줬다.
    (현재 요구되는 스펙이 일단은 name필드만 수정하게 만들어놓았기 때문이다.)



✍🏻 5-4 카테고리-삭제-테스트()

  • 삭제 테스트에서는 예외를 발생시켜서 그 예외 메세지로 assertThat을 사용해 볼것이다.
@Test
    public void 카테고리_삭제_테스트 () {
        //given
        CategoryDTO categoryDTO = createCategoryDTO("TestBranch", "TestCode", "TestName");
        Long savedId = categoryService.saveCategory(categoryDTO);
        //when

        categoryService.deleteCategoryOld(savedId);

        //then

        IllegalArgumentException e =
                assertThrows(IllegalArgumentException.class, 
                () -> categoryService.findCategory(savedId));
        assertThat(e.getMessage()).isEqualTo("찾는 카테고리 없습니다.");

    }

🗝 key points

  • IllegalArgumentException 객체를 만들고, assertThrows를 사용하여 findCategory() 에서 예외를 발생시키고, e라는 변수에 그 예외를 저장했다.

  • e.getMessage()findCategory()에서 예외가 터질때 출력하도록 만든 메세지와 비교하여 예외가 제대로 터졌는지 비교했다.

🙏🏻 테스트 코드 실행

  • IntelliJ 우측에 Gradle을 통해서 열어보면 test를 확인할 수 있다.
  • 이걸 더블클릭하면 모든 test들이 실행된다.
  • 만약 하나의 test만 실행하고 싶다면, 실행하고 싶은 test 메소드만 실행하면 된다.

  • 이렇게 초록색으로 좌르륵 테스트가 통과될때가 기분이 정말 좋다.
  • TDD는 개발자가 꼭 가져야 할 습관인 것 같다.

  • TEST 코드를 정교하고 정밀하게 짜놓으면 개발이 더 쉽고 즐거워진다.

  • 다음 포스팅에서는 api 통신을 반환하기 위한 controllerRefactor작업을 다루도록 하겠다.
    요구 스펙이 또 변해서... Refactor를 해야한다

profile
인문학 하는 개발자 💻
post-custom-banner

0개의 댓글