[테스트 코드]테스트 코드 작성 가이드

uijin kim·2023년 7월 3일
0

단위 테스트 사용 방법: Junit 참조 가이드
Gradle 프로젝트에 jacoco 설정하기 | 우아한 형제들

중구난방 테스트 코드 통일하자

AS-IS
Controller -> RestDocs 기반 통합 테스트 진행
Service → DB 혹은 다른 Layer와 의존성을 가지는 단위 테스트와 Mock 기반 테스트가 혼재되어 있음
Repository → 일부 작성
Entity, Dto → 미작성
Utils → 일부 작성

1. 테스트 시점

Github Pre-Commit (git hook 을 활용한 pre commit, pre push) - Remote Branch Push 전 테스트 강제
Jenkins 배포 시점 ( 현재 Gradle 빌드 시점에 Default로 적용되고 있음 )

2. 테스트 패키지 구조

구현 코드의 패키지 구조와 동일하게 구성

3. 클래스 및 메서드 명명 규칙

Unit test naming best practices - 단위 테스트 명명 규칙 모범 사례
Naming standards for unit tests - Roy Osherove - 애자일 컨설턴트의 단위 테스트 명명 규칙 가이드
[TDD/스크랩&번역] 7가지 유닛테스트 네이밍 - 7가지 유닛테스트 네이밍

클래스

테스트 대상 클래스 이름 + Test
Ex.
MembersControllerTest ( Controller )
MembersServiceTest ( Service )
BusinessMembersRepositoryTest ( Repository )
PasswordUtilsTest ( Utils )

메서드

  1. 메서드명테스트상태기대결과 (Ex. isAdult_AgeLessThan18_False)
    -> 코드 리팩토링으로 메서드명 변경시 테스트명 또한 변경되어야 한다.

  2. 메서드명기대결과테스트상태 (Ex. isAdult_False_AgeLessThan18)
    -> 1번과 비슷한 형태지만, 일부 개발자들은 해당 컨벤션 사용을 추천, 메서드명 변경시 테스트명또한 변경되어야한다.

  3. test[테스트할 기능](Ex. testIsNotAnAdultIfAgeLessThan18)
    -> 테스트할 기능이 테스트명의 일부로 사용되어 쉽게 읽을 수 있도록 해 준다. 그러나 "테스트" 접두사가 중복된다는 주장도 있다.

  4. 테스트할 기능
    → 테스트할 기능만 간단하게 쓰는 것이 더 낫다는 의견이 많다. 또한 코드의 악취를 방지하고, 문서화된 형태의 유닛 테스트를 수행하므로 권장되는 방법이다.

  5. should기대결과_When테스트상태 (Ex. IsNotAnAdultIfAgeLessThan18)
    -> 이러한 형태의 네이밍은 많은 사람들이 테스트에 대해 쉽게 이해할 수 있기 때문에 많이 사용된다.

  6. when테스트상태_Expect기대결과
    -> should_ThrowException_When_AgeLessThan18

  7. given사전조건_When테스트상태Expect기대결과
    -> BDD 기반의 네이밍 컨벤션이지만 테스트명이 너무 길어진다.
    Ex.Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail

4. Layer별 테스트 코드 작성 가이드

Dev-Tips/Spring-Boot-레이어별 테스트.md - (Naver Z) Layer 별 테스트

Controller - RestDocs를 이용하여 테스트 코드 작성중(강제)이며 @SpringBootTest 를 이용한 통합테스트로 작성한다.

Service -

테스트 하고자 하는 포인트에 집중할 수 있도록 Service Layer외에 모든 의존성을 끊어야 한다고 생각한다. 다른 Layer 개입하는 순간 Service 비즈니스 로직 테스트에 대한 관점이 흐려지고 타 Layer 를 신경쓰게 된다. (JPA Flush 존재하는 부분은 어떻게 테스트 할 것인가?)

각 테스트에는 given, when, then 으로 나누어 무엇을 기반으로, 어떤 것을 테스트 했을때, 어떤 결과를 기대하는지, 명확히 알 수 있도록 작성한다.

다른 Layer(Repository 혹은 다른 Service) 의존관계는 Mock 처리하며, DB 데이터검증은 Repository 테스트에서 진행한다.

Repository - @DataJpaTest 이용 하여 In-Memory 기반 테스트 코드 작성을 통해 나머지 Layer와 의존성을 끊자 (Repository ↔︎ Database)

Dto, Entity - Dto, Entity의 경우 클래스 내부 멤버 메서드들에 대한 코드 작성 필요 (Valid 관련 부분도 테스트 해야하나?)

Utils - AssertJ 기반 테스트 진행

5. 테스트 커버리지 (추후 고민)

Jacoco 이용해서 테스트 커버리지가 일정수치 넘지 않으면 commit 되지 않아야 하지 않을까? 너무 빡빡한가

6. 테스트를 위한 메서드 설계

Testable Code - 테스트 하기 쉬운 코드
How to Write more testable code - 어떻게 더 테스트하기 쉽게 코드를 작성할 수 있는가?

Service Layer의 단위 테스트를 진행할 때, 메서드의 호출 여부와 더불어 해당 메서드의 반환 값을 통해 테스트 유효성 검증이 일어나게 된다. 이 때 메서드의 반환 값이 void 일 경우 테스트 검증을 단순 메서드 호출여부로만 이루어져야 하기 때문에 테스트에 적합한 메서드의 리턴 값이 필요하다.

Select - 조회 대상 리턴

Insert - 생성된 데이터 혹은 키 값 리턴

Update - 업데이트 데이터 리턴

Delete - 삭제 대상 데이터 혹은 키 값 리턴

profile
느리더라도, 꾸준하게

0개의 댓글