Tdd 과정
TDD(Test Driven Development) 정리
1. TDD란?
TDD는 테스트 코드를 먼저 작성한 후 테스트를 통과시키기 위한 실제 코드를 구현하는 개발 방식이다.
1. 테스트 코드 작성
2. 컴파일 에러 / 실패 (빨간 줄)
3. 테스트를 통과시키는 실제 코드 작성
4. 리팩토링
2. Test 관련 Annotation
@SpringBootTest
Spring Boot 애플리케이션 전체를 통합 테스트할 때 사용
실제 설정된 DB 사용 가능
테스트 속도가 상대적으로 느림
@DataJpaTest
JPA 관련 컴포넌트만 빠르게 테스트할 때 사용
인메모리 DB(H2 등) 사용
실제 DB를 사용하고 싶다면 아래 어노테이션을 추가한다.
1
2
3
@AutoConfigureTestDatabase(
replace = AutoConfigureTestDatabase.Replace.NONE
)
@Disabled
- 테스트 실행을 비활성화
- 임시로 테스트를 제외하고 싶을 때 사용
@MockBean
@WebMvcTest에서 주로 사용- 가짜 객체(Mock)를 Spring Bean으로 등록
특징:
- Controller까지만 생성
- Service, Repository는 실제 객체 생성 ❌
- 협력 객체는
@MockBean으로 대체
1
2
3
Controller O
Service X → Mock
Repository X
3. SpringBootTest에서 Mock 사용
Mock이란?
Mock은 가짜 객체로 객체의 행위를 검증하기 위해 사용된다.
테스트 더블(Test Double)
- 테스트를 진행하기 어려운 객체를 대신하는 객체
- Mock은 테스트 더블의 한 종류
SpringBootTest + Mockito
SpringBootTest 환경에서 테스트 더블을 사용할 때 주로 Mockito를 사용한다.
Mockito의 주요 기능은 다음과 같다.
1
2
3
4
1. Mock 생성 (Create Mock)
2. 동작 정의 (Stub)
3. 사용 (Exercise)
4. 검증 (Verify)
@Mock vs @MockBean
@Mock
- Spring Container 필요 ❌
- 단순 단위 테스트
@MockBean
- Spring Container 필요 ⭕
- Bean이 컨테이너에 존재해야 할 때 사용
Spring Context가 필요하고 해당 객체가 Bean이라면 → @MockBean, 그렇지 않다면 → @Mock
4. Model 관련 Annotation
@Entity
- JPA가 관리하는 클래스
- DB 테이블과 매핑되는 객체
@Data
Lombok 어노테이션을 모두 포함한다.
1
2
3
4
5
@ToString
@EqualsAndHashCode
@Getter
@Setter
@RequiredArgsConstructor
*개별 옵션을 세밀하게 제어해야 할 경우 @Data 대신 개별 어노테이션 사용 권장
5. @Builder 패턴
Builder를 사용하는 이유
생성자 파라미터가 많아질수록 가독성이 떨어지고 매개변수 순서를 헷갈리기 쉽다
1
2
Registry registry =
new Registry("hello", "hi", "uni", "etc");
위 코드는 각 값이 어떤 의미인지 한눈에 파악하기 어렵다.
Builder 패턴 적용
1
2
3
4
5
6
7
8
public Registry toEntity() {
return Registry.builder()
.id(registryId)
.title(registryTitle)
.main(registryMain)
.writer(registryWriter)
.build();
}
- 어떤 값이 어떤 필드에 들어가는지 명확
- 가독성 향상
- 선택적 파라미터 처리 용이
6. this를 이용한 객체 필드 초기화
1
2
3
4
5
6
public Registry(RegistryDto registryDto) {
this.registryId = registryDto.getRegistryId();
this.registryTitle = registryDto.getRegistryTitle();
this.registryMain = registryDto.getRegistryMain();
this.registryWriter = registryDto.getRegistryWriter();
}
this는 현재 객체를 가리키며
Dto로부터 전달받은 값을 Entity의 필드에 초기화하기 위해 사용된다.
Error
Controller를 tdd Post 코드를 작성 하면서 오류를 해결한 과정을 적어봤다.
☝🏻 DTO
처음에 내가 dto에 값을 담아준 것으로 생각하고 왜 에러가 떴는지 몰랐다.
알고보니 dto에 값을 담아준 것이 아니라 이 자리에 BoardDto가 들어와야 한다고 알려준 것이다.
그리고 dto로 설정에서 dto에 담아서 보내줘야 한다.
그래서 new BoardDto를 작성해준 것이다.
GET도 dto로 작성?
내가 작성한 get 코드는 아래처럼 dto를 사용하지 않았다.
1
2
3
given(boardService.getRegistry("23")).willReturn(
new Board("23", "title", "main", "writer")
);
그 이유는 본 코드를 보면 알 수 있는데 파라미터를 post에서는 dto를 담고 있고
get에서는 String BoardId를 담고 있었기 때문에 dto가 아닌 값을 주어서 dto를 사용하지 않은 것이다.
무조건 dto를 쓰는 것이 아니라 어떻게 짜냐에 따라 쓸 수 있고 안쓸 수도 있다.
✌🏻 메모리 주소가 달라서 생기는 오류
1번에서 말한 대로 수정한 후에 실행을 했더니 에러가 나타났다.
ERROR : java.lang.AssertionError: No value at JSON path "$.boardId"
new Dto → any(Dto.class)로 해결
1
2
given(boardService.saveRegistry(new BoardDto("23","title", "main", "writer")))
.willReturn(new Board("23","title", "main", "writer"));
⬇️
1
2
given(boardService.saveRegistry(any(BoardDto.class)))
.willReturn(new Board("23","title", "main", "writer"));
new BoardDto(~~) → any(BoardDto.class)로 변경했다.
RegistryDto 클래스 타입으로 들어오는 값을 사용한다는 의미로
new 로 새로운 객체를 만들어서 작성하지 않고 구현체 객체를 사용해 메모리 주소가 다르지 않아서 오류가 해결 된 것 같다.
본 코드처럼 dto는 들어오는 값을 쓴다라는 느낌
REFERENCE

