단위 테스트
테스트는 왜 필요할까?
테스트 코드를 작성하지 않는다면
- 변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.
- 변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.
- 빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.
테스트 코드가 병목이 된다면
- 프로덕션 코드의 안정성을 제공하기 힘들어진다.
- 테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.
- 잘못된 검증이 이루어질 가능성이 생긴다.
올바른 테스트 코드는
- 자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.
- 소프트웨어의 빠른 변화를 지원한다.
- 팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.
- 가까이 보면 느리지만, 멀리 보면 가장 빠르다.
JUnit5로 테스트하기
단위 테스트
- 작은 코드 단위를 독립적으로 검증하는 테스트
- 검증 속도가 빠르고, 안정적인다.
Junit 5
- 단위 테스트를 위한 테스트 프레임워크
- XUnit - Kent Beck
- SUnit(Smalltask), Junit(Java), Nunit(.NET), ...
AssertJ
- 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리
- 풍부한 API, 메서드 체이닝 지원
테스트 케이스 세분화하기
경계값 테스트 - 범위(이상, 이하, 초과, 미만), 구간, 날짜 등
테스트하기 어려운 영역을 분리하기
- 테스트하기 어려운 영역을 구분하고 분리하기
- 외부로 분리할수록 테스트 가능한 코드는 많아진다.
테스트하기 어려운 영역
- 관측할 때마다 다른 값에 의존하는 코드
- 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등
- 외부 세계에 영향을 주는 코드
- 표준 출력, 메시지 발송, 데이터베이스에 기록하기 등
순수함수 (pure functions)
- 같은 입력에는 항상 같은 결과
- 외부 세상과 단절된 형태
- 테스트하기 쉬운 코드
TDD: Test Driven Development
- 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론
RED, GREEN, REFACTOR
- RED: 실패하는 테스트 작성
- GREEN: 테스트 통과 최소한의 코딩
- REFACTOR: 구현 코드 개선 테스트 통과 유지
선 기능 구현, 후 테스트 작성
- 테스트 자체의 누락 가능성
- 특정 테스트 케이스(해피 케이스)만 검증할 가능성
- 잘못된 구현을 다소 늦게 발견할 가능성
선 테스트 작성, 후 기능 구현
- 복잡도(유연하며 유지보수가 쉬운)가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다.
- 쉽게 발견하기 어려운 엣지(Edge) 케이스를 놓치지 않게 해준다.
- 구현에 대한 빠른 피드백을 받을 수 있다.
- 과감한 리팩토링이 가능해진다.
TDD: 관점의 변화
- 테스트는 구현부 검증을 위한 보조 수단 -> 테스트와 상호 작용하며 발전하는 구현부
- 클라이언트 관점에서의 피드백을 주는 Test Driven
테스트는 []다.
테스트는 문서다.
- 프로덕션 기능을 설명하는 테스트 코드 문서
- 다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완
- 어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다.
우리는 항상 팀으로 일한다.
DisplayName을 섬세하게
-> 음료 1개 추가 테스트 <- "~테스트" 지양하기
-> 음료를 1개 추가할 수 있다.
-> 음료를 1개 추가하면 주문 목록에 담긴다.
- 명사의 나열보다 문장으로 - A이면 B이다. A이면 B가 아니고 C다.
- 테스트 행위에 대한 결과까지 기술하기
-> 특정 시간 이전에 주문을 생성하면 실패한다.
-> 영업 시작 시간 이전에는 주문을 생성할 수 없다.
- 도메인 용어를 사용하여 한층 추상화된 내용을 담기
- 테스트의 현상을 중점으로 기술하지 말 것
Spring & JPA 기반 테스트
레이어드 아키텍처(Layered Architecture)와 테스트
Layered Architecture
- Presentation Layer
- Business Layer
- Persistence Layer
테스트 하기 복잡해 보인다?
통합 테스트(Integration test)
- 여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트
- 일반적으로 작은 범위의 단위 테스트만으로는 기느으 전체의 신뢰성을 보장할 수 없다.
- 풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트
Spring / JPA 훑어보기 & 기본 엔티티 설계
Library vs Framework
- Library는 내 코드가 주체가 되는 능동적 환경.
- Framework 내 코드가 프레임 안에서 역할을 하게 되는 수동적 환경.
Spring
- IoC(Inversion of Control)
- DI(Dependency Injection)
- AOP(Aspect Oriented Programming)
ORM(Object-Relational Mapping)
- 객체 지향 패러다임과 관계형 DB 패러다임의 불일치
- 이전에는 개발자가 객체의 데이터를 한땀한땀 매핑하여 DB에 저장 및 조회(CRUD)
- ORM을 사용함으로써 개발자는 단순 작업을 줄이고, 비즈니스 로직에 집중할 수 있다.
JPA(Java Persistence API)
- Java 진영의 ORM 기술 표준
- 인터페이스이고, 여러 구현체가 있지만 보통 Hibernate를 많이 사용한다.
- 반복적인 CRUD SQL을 생성 및 실행해주고, 여러 부가 기능을 제공한다.
- 편리하지만 쿼리를 직접 작성하지 않기 때문에, 어떤 식으로 쿼리가 만들어지고 실행되는지 명확하게 이해하고 있어야 한다.
- Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공
- QueryDSL과 조합하여 많이 사용한다. (타입체크, 동적쿼리)
- @Entity, @Id, @Column
- @ManyToOne, @OneToMany, @OneToOne, @ManyToMany(일대다 - 다대일 관계로 풀어서 사용)
엔티티 설계
- Order
- OrderProduct
- Product
출처