[인프런 워밍업 스터디 클럽 2기 백엔드] 3주차
해당 글은 인프런 박우빈 강사님의 「Practical Testing: 실용적인 테스트 가이드」을 바탕으로 작성하였습니다.
3주차 요약
테스트의 필요성
테스트란 본래 귀찮은 존재이지만, 꼭 거쳐야만 한다...!
테스트 코드가 없으면 변화의 순간마다 모든 케이스를 고려해야만 한다.
그 변화들에 대한 내용을 모든 팀원이 알고 인지하고 고민해야 한다.
안정성 보장이 불가능해진다.
수동 테스트의 문제
커버 불가능한 영역이 발생한다.
경험과 감에만 의존한 코드가 되어버린다.
피드백이 늦어진다.
유지 보수가 어려워진다.
⇒소프트웨어에 대한 전체적 신뢰도가 낮아진다.
엉망인 테스트 코드의 문제
안정성 제공이 어려워진다.
테스트 코드 자체의 유지 보수가 어려워진다.
잘못된 검증이 일어날 수 있다.
올바른 테스트 코드?
자동화 테스트를 통한 빠른 버그가 발견 가능하다.
수동 테스트에 드는 비용이 절약된다.
소프트웨어의 빠른 변화를 지원한다.
테스트 코드 작성할 때는 정말 오래 걸리고 번거로운 작업이지만, 멀리 보면 결과적으로 가장 빠른 길이 된다. 아무리 귀찮고 간단한 로직이라도 항상 무조건 테스트해야 한다.
단위 테스트
작은 코드 단위(클래스/메서드)를 독립적으로 검증하는 테스트
검증 속도가 빠르고 안정적이다.
JUnit5
단위 테스트를 위한 자바 진영의 테스트 프레임워크
AssertJ
테스트 코드 작성을 원활하게 돕는 테스트 라이브러리
풍부한 API, 메서드 체이닝을 제공한다.
테스트 케이스의 세분화
암묵적이거나 아직 드러나지 않은 요구사항이 있는지 언제나 질문하는 것이 중요하다.
해피 케이스와 예외 케이스의 경계 값으로 테스트를 한다.
테스트하기 어려운 파트를 구분하기
현재 시간, 사용자의 입력 값 등 언제나 달라질 수 있는 값이나 외부 세계에 영향을 주는 코드는 외부로 분리한다.
테스트 하고자 하는 영역을 잊지 않고 집중해야 한다.
테스트 하기 좋은 함수는 순수함수다.
같은 입력 값에는 항상 동일한 결과를 도출한다.
외부 세계와 단절되어 있다.
lombok
@Data, @Setter, @AllArgsConstructor은 지양한다.
@ToString은 JPA 양방향 관계 설정 시 순환 참조의 문제가 발생할 수 있어 DTO 외에서는 지양한다.
TDD ★
프로덕션 코드보다 테스트 코드를 우선적으로 작성해 테스트가 구현을 주도하도록 하는 방법론이다.
레드 그린 리팩토링을 사용한다.
레드: 구현부가 없어 실패하는 테스트 작성
그린: 빠른 시간 내에 테스트를 통과하도록 최소한의 코딩
리팩토링: 구현 코드를 개선하며 테스트의 통과를 유지
피드백 (TDD 핵심 가치)
복잡도가 낮은 테스트 가능한 코드를 구현 가능케 한다.
테스트 자체의 누락 가능성이 낮아진다.
잘못된 구현을 빠르게 발견할 수 있다.
발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.
과감한 리팩토링이 가능해진다.
테스트는 또 하나의 문서
기능을 설명하는 테스트 코드 문서이기 때문에 프로덕션 코드를 이해하는 시각과 관점을 보완해준다.
displayName의 작성법
'~~ 테스트' 같은 문장을 압축한 형식의 작명은 지양한다.
최대한 도메인 용어와 결과까지 한 문장으로 작성해, 어느 누가 봐도 이해 가능하도록 작성한다.
테스트는 메서드 관점이 아닌 도메인 정책의 관점으로 바라본다.
BDD ★
given / when / then 형식의 시나리오에 기반한 테스트 케이스 자체에 집중한다.
개발자가 아닌 사람이 봐도 이해 가능할 정도의 추상화 레벨을 권장한다.
Given: 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)
When: 시나리오 행동 진행
Then: 시나리오에 대한 결과 명시 / 검증
레이어드 아키텍쳐
관심사의 분리로 별도 단위 테스트를 가능하게 한다.
프레젠테이션 레이어 / 비즈니스 레이어 / 퍼시스턴트 레이어 로 구분된다.
통합 테스트
A 모듈과 B 모듈을 융합한 결과가 AB일지 C일지 알 수가 없기 때문에 단위 테스트로는 부족하다.
⇒ 통합 테스트의 필요
여러 모듈이 협력하는 기능을 통합적으로 검증한다.
기능 전체의 신뢰성을 보장한다.
테스트는 풍부한 단위 테스트와 큰 기능 단위를 검증하는 통합 테스트의 모음이다.
스프링은 라이브러리 ? 프레임워크 ?
라이브러리는 내 코드가 주체, 프레임워크는 프레임워크가 주체가 된다.
이미 존재하는 프레임 안에 형식에 맞게 내 코드를 끼워넣는 것 이다.
스프링의 3대 개념
제어의 역전(IoC)
의존성 주입(DI)
AOP - 비즈니스 흐름과 관계없는 부분을 하나로 모아 다른 모듈로 분할
JPA(자바 퍼시스턴트 API)
자바 진영의 ORM이자 인터페이스이다.
여러 구현체 중 하이버네이트를 주로 사용한다.
편리하지만 쿼리를 직접 작성하지 않기 때문에 어떤 식으로 쿼리가 생성 후 실행되는지 명확한 이해가 필요하다.
JPA를 한번 더 추상화한 스프링 데이터 JPA 제공한다.
쿼리 DSL과 조합해 많이 사용한다. (타입 체크, 동적 쿼리)
ORM(오브젝트 릴레이셔널 매핑)
객체 지향 패러다임과 관계형 DB 패러다임과의 불일치하기 때문에 탄생한 매핑 개념이다.
이전 개발자가 객체의 데이터를 하나하나 매핑하여 저장 및 조회를 실행해야 했다면, 현재는
ORM을 통해 개발자는 단순 작업을 줄이고 비즈니스 로직에 집중할 수 있다.
Persistence Layer
데이터와 직접 접근하는 레이어이다.
비즈니스 가공 로직이 포함되면 안 된다.
데이터의 단순 CRUD에 집중한다.
Business Layer
비즈니스 로직을 구현하는 레이어이다.
Persistence Layer와 상호작용해 로직을 전개한다.
트랜잭션의 보장이 필요하다.
+α
재고 감소나 데이터 추가 같은 부분은 동시성 이슈가 발생 가능성이 높기에, optimistic lock 이나 pessimistic lock 등의 방법을 통해 해결한다. ← 추가 학습 필요
데이터 추가 같은 부분의 동시성 이슈가 발생했을 때, 사용자의 요청을 아예 무시할 수는 없다. 데이터 엔티티의 인덱스에 유니크 인덱스 설정을 하고 시스템 내에서 자동적으로 재시도를 n회 하는 등의 방식으로 해결 가능하다.
미션
미션 4는 Readable code 강의의 예제를 바탕으로 단위 테스트를 작성하는 과제였다. Practical testing 강의 내의 예제는 꽤나 간단했기에, 이번 과제는 단순한 과제라고 생각하고 가벼운 마음으로 접근했지만 역시나 만만치 않았다. 특히나 지뢰찾기 게임 프로젝트도 스터디 카페 프로젝트도 사용자의 입력에 관여하는 로직이 많아 mock 객체를 사용하지 않고 테스트하는 방법이 도무지 떠오르지 않아, 결과적으로는 내가 직접 접근이 가능한 부분에 대해서만 짧게 테스트 코드를 작성하는 것으로 마무리했다. 어떤 클래스에서도 모든 케이스에 대한 테스트를 진행하지 못한 것이 너무나도 아쉬웠다.😂
댓글을 작성해보세요.