인프런 워밍업 클럽 스터디 2기 - 백엔드 클린코드, 테스트코드 3주차 발자국
3주차부터 Practical Testing (실용적인 테스트 가이드) 강의로 넘어가게 되었다.
Section1) Intro
강의에서 학습하게 될 것
1) 테스트 코드가 필요한 이유
2) 좋은 테스트 코드란?
3) 실제 실무에서 진행하는 방식 그대로 테스트를 작성해가면서 API를 설계하고 개발하는 방법
4) 정답은 없지만, 오답은 있다! 구체적인 이유에 근거한 상세한 테스트 작성 팁
Section2) 테스트는 왜 필요할까?
1) 테스트는 왜 필요할까?
- 테스트 코드를 작성하지 않는다면 코드의 변화가 생기는 매 순간마다 발생할 수 있는 모든 Case를 고려해야 한다.
.
2) 테스트 코드가 병목이 된다면?
- 테스트코드 자체가 유지보수하기 어려워진다.
- 잘못된 검증이 이루어질 가능성이 생긴다.
.
3) 올바른 테스트 코드는?
- 자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.
Section3) 단위 테스트
Junit5로 테스트하기
1) 단위 테스트
- 작은 (ex, 클래스 or 메서드) 코드 단위를 독립적으로 검증하는 테스트
- 검증속도가 빠르고, 안정적이다
.
2) Junit5
- 단위 테스트를 위한 테스트 프레임워크
- XUnit - Kent Back,
- Sunit (Samltalk), Junit(Java), NUnit(.NET)
- Spring-boot-starter-test 라이브러리를 통해 사용 가능하다.
.
3) AssertJ
- 테스트코드 작성을 원활하게 돕는 테스트 라이브러리
- 풍부한 API, 메서드 체이닝 지원
- Spring-boot-starter-test 라이브러리를 통해 사용 가능하다.
테스트 케이스 세분화
1) 해피 케이스
- 요구사항을 그대로 만족하는 케이스
- 경계값 테스트 : 범위 (이상, 이하, 초과, 미만), 구간, 날짜 등이 중요
.
2) 예외 케이스
- 경계값 테스트 : 범위 (이상, 이하, 초과, 미만), 구간, 날짜 등이 중요
.
테스트하기 어려운 영역 분리하기
1) 테스트하기 어려운 영역
- 관측할때마다 다른 값에 의존하는 코드
→ 현재 날짜/시간, 랜덤 값, 전역변수/함수, 사용자 입력 등
- 외부 세계에 영향을 주는 코드
→ 표준 출력, 메시지 발송, 데이터베이스에 기록하기 등
.
2) 순수 함수 - 테스트하기 쉬운 코드
- 같은 입력에는 항상 같은 결과
- 외부 세상과 단절된 형태
- 테스트하기 쉬운 코드
Section4) TDD
TDD (Test Driven Development)
1) TDD
- 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론
.
2) TDD의 핵심 가치
- 기존) 선 기능 구현, 후 테스트 작성의 문제점
→ 테스트 자체의 누락 가능성
→ 특정 테스트(==해피 케이스) 케이스만 검증할 가능성
→ 잘못된 구현을 다소 늦게 발견할 가능성
- TDD) 선 테스트 작성, 후 기능 구현
→ 복잡도가 낮은(==유연하며 유지보수가 쉬운), 테스트 가능한 코드로 구현할 수 있게 한다.
→ 쉽게 발견하기 어려운 엣지(Edge) 케이스를 놓치지 않게 해준다.
→ 구현에 대한 빠른 피드백을 받을 수 있다.
→ 과감한 리팩토링이 가능해진다.
Section5) 테스트는 [문서]다
테스트는 [문서]다
1) 테스트 == 문서
- 프로덕션 기능을 설명하는 테스트 코드 문서
- 다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완
- 어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유
.
DisplayName을 섬세하게
1) DisplayNamed을 섬세하게 적기
- 메서드 자체의 관점보다 도메인 정책 관점으로, 도메인 용어를 사용하여 한층 추상화된 내용을 담기
- 테스트의 현상(ex, 성공한다… 실패한다 등)을 중점으로 기술하지 말 것
.
BDD 스타일로 작성하기
1) BDD (Behavior Driven Development)
- TDD에서 파생된 개발 방법으로
- 함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트하는 기법
- Given / When / Then 방식 사용
→ Given : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)
→ When : 시나리오 행동 진행
→ Then : 시나리오 진행에 대한 결과 명시, 검증
.
Mission 12) 단위 테스트 작성
studycafe 프로젝트에서 InputHandler, StudyCafePassOrder, StudyCafeSeatPass 클래스에 대한 테스트 코드를 작성하였다.
고민했던 점으로는 Scanner를 통해 사용자 입력을 받는 로직을 테스트하기 위해 Mock을 사용할 수 있지만, Mock 대신 InputStream과 System.setIn()을 통해 입력해주는 방식을 선택했다.
작은 메서드 단위로 단위 테스트를 진행하니 코드도 복잡해지지 않고 간결하여 테스트 하기가 수월하였다.
.
Section6) Spring & JPA 기반 테스트
레이어드 아키텍처(Layered Architecture)
1) Layered Architecture
- Presentation Layer, Business Layer, Persistence Layer로 구분
- 아키텍처를 분리하는 이유 : 관심사의 분리
.
Spring / JPA 훑어보기 & 기본 엔티티 설계
1) Library vs Framework
- Library : 외부에서 이미 개발된 코드를 가져온다. (내 코드가 주체가 되어 동작)
- Framework : 이미 동작하는 환경이 구성되어 있고, 내 코드는 프레임안에서 수동적으로 동작
.
2) Spring
- IoC (inversion of Control) : 객체의 생성과 의존성 관리를 프레임워크에서 대신 수행
- DI (Dependency Injection) : 외부에서 객체 간의 의존성 주입을 통해 결합도를 낮춰줌
- AOP (Aspect Oriented Programming) : 공통 관심사를 별도의 로직으로 분리해 코드 중복을 줄여주고 모듈화
.
3) JPA
- Java Persistance API
- Java 진영의 ORM 기술 표준
- 반복적인 CRUD SQL을 생성 및 실행해주고, 여러 부가 기능들을 제공
- Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공
- 주로 사용되는 어노테이션들
→ @Entity, @Id, @Column
→ @ManyToOne, @OneToMany, @OneToOne, @ManyToMany
.
Layer별 테스트
1) Persistence Layer
- Data Access의 역할
- 비즈니스 가공 로직이 포함되어서는 안됨, Data에 대한 CURD에만 집중한 레이어
.
2) Business Layer
- 비즈니스 로직을 구현하는 역할
- Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)를 통해 비즈니스 로직을 전개
- 트랜잭션을 보장
.
3) Presentation Layer
- 외부 세계의 요청을 가장 먼저 받는 계층
- 파라미터에 대한 최소한의 검증을 수행
.
회고
- 테스트 코드 작성을 의무적으로 작성 하지 않는 환경에서 일을 해왔는데, 스터디를 진행하면서 단위 테스트 코드 작성의 중요성과 필요성을 깨달았다. 모든 예외 케이스를 조기에 발견해서 대처할 순 없겠지만 내가 작성한 코드 범위내에서 테스트가 필요한 영역들을 분리하여 로직이 의도한 대로 동작하는지 검증하는 작업은 백엔드 개발자의 숙명이라는 생각이 들었다.
이제 Mock과 테스트 방법론에 대한 강의가 남아있는데, 마지막 주차도 지금처럼 묵묵히 학습해 나갈 것이다.
출처
댓글을 작성해보세요.