diff --git "a/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270.md" "b/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270.md" deleted file mode 100644 index a1fa995..0000000 --- "a/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270.md" +++ /dev/null @@ -1,71 +0,0 @@ -## 단위 테스트(Unit Test) - -> 자동화된 테스트 기법 -> - 단위 테스트 -> - 통합 테스트 -> - 인수 테스트 - - -### 단위 테스트 -- **작은** 코드 단위를 **독립적**으로 검증하는 테스트 - - 작은 코드 단위: 클래스 or 메서드 단위 -- 외부에 의존하지 않기 때문에 검증 속도가 빠르고 안정적이다. - -
- -#### JUnit5 -- XUnit: 단위 테스트를 위한 테스트 프레임워크 - - Java에서는 `JUnit`, .Net에서는 Nunit 등등 - -
- -#### AssertJ - -- 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리 -- 풍부한 API 및 메서드 체이닝 지원 - - - -
- -### 테스트 케이스 세분화하기 - -- `한 종류의 음료 여러 잔을 한 번에 담는 기능` 요구 사항 추가 되었을 때 항상 자신에게 혹은 기획자 또는 디자이너에게 질문을 할 수 있어야 한다. - - 질문하기: 이 요구사항이 실제 내가 구현할 때 그 요구사항과 정획히 맞아떨어지는가? 암무적이거나 아직 드러나지 않은 요구사항이 있는가? - - 위의 요구사항은 해피케이스에 대한 내용만 존재하고, 예외 케이스에 대한 요구사항은 나와있지 않다(암묵적이다). -- 테스트 케이스를 세분화하고, 경계값이 존재하면 **경계값**에서 테스트를 하는 것이 중요하다. - - `해피 케이스(요구사항을 그대로 만족하는 테스트 케이스)`, `예외 케이스(그 외 케이스)` - - 이런 케이스들을 테스트할 때는 `경계값 테스트`가 중요하다. - - 경계값 테스트: 범위(이상, 이하, 초과, 미만), 구간, 날짜 등 - -
- -### 테스트하기 어려운 영역을 구분하고 분리하기 - -- `가게 운영 시간(10:00 ~ 22:00)외에는 주문을 생성할 수 없다`가 요구 사항에 추가 되었을 때 - - 주문을 생성하는 함수 안에서 현재 시간을 받아온다면, 해당 기능에 대한 테스트 코드는 테스트를 수행하는 시간에 따라 결과가 다르게 나온다. -- `현재 시간`이 테스트를 어렵게 만드는 요소로 파악된다면, 현재 시간을 파라미터로 받아오도록 수정해보자 - - 파라미터에 테스트하고자 하는 시간(가게 오픈 시간의 경계값)을 넣음으로써 언제든지 원하는 테스트를 수행할 수 있다. -- 결국 테스트하기 어려운 영역을 외부로 분리(파라미터로 받기)하자 - - 외부로 분리할수록 테스트 가능한 코드는 많아진다. - -> 테스트하고자 하는 것은 **어떤 시간이 주어졌을 때, 시간 범위 안에 이 시간이 들어오는 것이 중요한 것**이지 현재 시간이 중요한 것은 아니다. 따라서 테스트 코드 상에서 원하는 값을 넣어줄 수 있도록 설계를 변경하는 것이 중요하다. - -
- -#### 테스트하기 어려운 영역 - -- 관측할 때마다 다른 값에 의존하는 코드 -> input 역할 - - 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등 -- 외부 세계에 영향을 주는 코드 -> output 역할 - - 표준 출력, 메시지 발송, 데이터베이스에 기록하기 등 - -
- -#### 테스트하기 쉬운 영역(순수함수) - -- 같은 입력에는 항상 같은 결과 -- 외부 세상과 단절된 형태 -- 테스트하기 쉬운 코드 - - diff --git "a/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270\354\231\200 TDD.md" "b/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270\354\231\200 TDD.md" new file mode 100644 index 0000000..13d8271 --- /dev/null +++ "b/Testing/2. \353\213\250\354\234\204 \355\205\214\354\212\244\355\212\270\354\231\200 TDD.md" @@ -0,0 +1,243 @@ +## 2. 단위 테스트와 TDD + +
+ +### 목차 +- [단위 테스트(Unit Test)](#단위-테스트(Unit-Test)) +- [TDD(Test Driven Development)](#TDD(Test-Driven-Development)) +- [테스트는 [문서]다.](#테스트는-[문서]다.) + +
+
+ +## [단위 테스트(Unit Test)](#목차) + +> 자동화된 테스트 기법 +> - 단위 테스트 +> - 통합 테스트 +> - 인수 테스트 + + +### 단위 테스트 +- **작은** 코드 단위를 **독립적**으로 검증하는 테스트 + - 작은 코드 단위: 클래스 or 메서드 단위 +- 외부에 의존하지 않기 때문에 검증 속도가 빠르고 안정적이다. + +
+ +#### JUnit5 +- XUnit: 단위 테스트를 위한 테스트 프레임워크 + - Java에서는 `JUnit`, .Net에서는 Nunit 등등 + +
+ +#### AssertJ + +- 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리 +- 풍부한 API 및 메서드 체이닝 지원 + + + +
+ +### 테스트 케이스 세분화하기 + +- `한 종류의 음료 여러 잔을 한 번에 담는 기능` 요구 사항 추가 되었을 때 항상 자신에게 혹은 기획자 또는 디자이너에게 질문을 할 수 있어야 한다. + - 질문하기: 이 요구사항이 실제 내가 구현할 때 그 요구사항과 정획히 맞아떨어지는가? 암무적이거나 아직 드러나지 않은 요구사항이 있는가? + - 위의 요구사항은 해피케이스에 대한 내용만 존재하고, 예외 케이스에 대한 요구사항은 나와있지 않다(암묵적이다). +- 테스트 케이스를 세분화하고, 경계값이 존재하면 **경계값**에서 테스트를 하는 것이 중요하다. + - `해피 케이스(요구사항을 그대로 만족하는 테스트 케이스)`, `예외 케이스(그 외 케이스)` + - 이런 케이스들을 테스트할 때는 `경계값 테스트`가 중요하다. + - 경계값 테스트: 범위(이상, 이하, 초과, 미만), 구간, 날짜 등 + +
+ +### 테스트하기 어려운 영역을 구분하고 분리하기 + +- `가게 운영 시간(10:00 ~ 22:00)외에는 주문을 생성할 수 없다`가 요구 사항에 추가 되었을 때 + - 주문을 생성하는 함수 안에서 현재 시간을 받아온다면, 해당 기능에 대한 테스트 코드는 테스트를 수행하는 시간에 따라 결과가 다르게 나온다. +- `현재 시간`이 테스트를 어렵게 만드는 요소로 파악된다면, 현재 시간을 파라미터로 받아오도록 수정해보자 + - 파라미터에 테스트하고자 하는 시간(가게 오픈 시간의 경계값)을 넣음으로써 언제든지 원하는 테스트를 수행할 수 있다. +- 결국 테스트하기 어려운 영역을 외부로 분리(파라미터로 받기)하자 + - 외부로 분리할수록 테스트 가능한 코드는 많아진다. + +> 테스트하고자 하는 것은 **어떤 시간이 주어졌을 때, 시간 범위 안에 이 시간이 들어오는 것이 중요한 것**이지 현재 시간이 중요한 것은 아니다. 따라서 테스트 코드 상에서 원하는 값을 넣어줄 수 있도록 설계를 변경하는 것이 중요하다. + +
+ +#### 테스트하기 어려운 영역 + +- 관측할 때마다 다른 값에 의존하는 코드 -> input 역할 + - 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등 +- 외부 세계에 영향을 주는 코드 -> output 역할 + - 표준 출력, 메시지 발송, 데이터베이스에 기록하기 등 + +
+ +#### 테스트하기 쉬운 영역(순수함수) + +- 같은 입력에는 항상 같은 결과 +- 외부 세상과 단절된 형태 +- 테스트하기 쉬운 코드 + + +
+
+ +## [TDD(Test Driven Development)](#목차) + +> [TDD 참고](https://github.com/jmxx219/CS-Study/blob/main/etc/TDD.md) + +
+ +- 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론 + - 지금까지는 프로덕션 코드를 먼저 만들고 테스트 코드를 작성해왔는데 이 순서를 변경하는 것 +- TDD 개발 주기 + - `RED` : 실패하는 테스트 작성 + - 아직 구현부(프로덕션 코드)가 없기 떄문에 실패함 + - `GREEN` : 테스트를 통과하기 위한 최소한의 코딩 + - 빠른 시일 내에 구현부를 작성하여 통과하기 + - `REFACTOR` : 구현 코드 개선. 테스트 통과 유지 + - 구현 코드 개선 및 테스트 통과 유지 + +
+ +### TDD로 테스트 만들기 + +1. 테스트 코드를 먼저 작성한다. + ```Java + class CafeKioskTest{ + ... + @Test + void calclulateTotalPrice(){ + CafeKiosk cafeKiosk = new CafeKiosk(); + Americano americano = new Americano(); + Latte latte = new Latte(); + + cafeKiosk.add(americano); + cafeKiosk.add(latte); + + int totalPrice = cafeKiosk.calculateTotalPrice(); + + assertThat(totalPrice).isEqualTo(8500); + } + } + ``` +2. 메서드가 컴파일 되도록 최소한의 코드만 작성한다. + ```Java + public class CafeKiosk{ + // 1단계: 최소한의 컴파일이 되도록 최소한의 코드 작성 + public int caculateTotalPrice(){ + return 0; + } + } + ``` +3. 빨간불이 뜬다. → `RED` 상태 +4. 빠른 시간 내에 초록불이 뜨게 만든다. → `GREEN` 상태 + ```Java + public class CafeKiosk{ + // 2단계: 빠른 시간 안에 초록불이 뜨게 만들기 + public int caculateTotalPrice(){ + return 8500; + } + } + ``` +5. 리팩토링을 한다. → `REFACTOR` 하기 + ```Java + public class CafeKiosk{ + // 3단계: 리팩토링 하기 + public int caculateTotalPrice(){ + int totalPrice = 0; + for(Beverage beverage : beverage) + totalPrice += beverage.getPrice(); + return totalPrice; + } + } + ``` + - 구현분를 완전히 개편하여도 테스트를 통과한다. → 과감한 리팩토링 가능 + ```Java + public class CafeKiosk{ + public int caculateTotalPrice() { + return beverages.stream().mapToInt(b -> b.getPrice()).sum(); + } + } + ``` + + +
+ +#### 선 기능 구현 후, 테스트 작성의 단점 +- 테스트 자체의 누락 가능성 +- 특정 테스트 케이스(해피 케이스)만 검증할 가능성 +- 잘못된 구현을 다소 늦게 발견할 가능성 + + +
+ +#### 선 테스트 작성 후, 기능 구현의 장점 + +- 복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다. + - 테스트를 먼저 작성하면, 테스트하기 위한 구조를 고민하게 되어 테스트하기 어려운 영역을 미리 분리할 수 있다. +- 쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다. +- 구현에 대한 빠른 피드백을 받을 수 있다. +- 과감한 리팩토링이 가능해진다. + + +> TDD는 클라이언트 관점에서 우리의 프로덕션 코드를 피드백해주는 Test Driven +> `RED` → `GREEN` → `REFACTOR`를 이용하여 TDD를 구현한다. + + +
+
+ +## [테스트는 [문서]다.](#목차) + + +### 문서 + +- 프로덕션 기능을 설명하는 테스트 코드 문서 +- 다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완 +- 어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다. + - 테스트 코드가 팀 자산으로 공유할 수 있다는 장점이 존재한다. + +> 우리는 항상 팀으로 일하기 때문에 내가 작성한 코드, 테스트를 다른 팀원이 이해할 수 있도록 문서로 잘 정리해야 한다. + +
+ +### DisplayName은 섬세하게 + +> 테스트 함수가 하는 일을 더 상세하게 알려줄 수 있다. + + +- 명사의 나열보다 문장으로 작성하기 + - `~ 테스트`로 끝나는 설명은 지양하자 +- 테스트 **행위**에 대한 **결과**까지 기술하기 +- 도메인 용어를 사용하여 한 층 추상화된 내용을 담기 + - 메서드 자체의 관점보다 도메인 정책 관점으로 작성하기 + - ex) `우리 카페 키오스크의 영업 시작 시간`이라는 도메인 용어를 사용하기 +- 테스트의 현상을 중점으로 기술하지 말 것 + +
+ +### BDD(Behavior Driven Development) 스타일로 작성하기 + +- TDD에서 파생된 개발 방법 +- 함수 단위의 테스트에 집중하기보다 **시나리오에 기반한 테스트케이스(TC)** 자체에 집중하여 테스트한다. +- 개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨)을 권장한다. + +
+ +#### Given / When / Then + +- Given : 시나리오 진행에 필요한 모든 준비 과정(객체, 값, 상태 등) +- When: 시나리오 행동 진행 +- Then: 시나리오 진행에 대한 결과 명시, 검증 + +> 어떤 환경에서(Given) 어떤 행동을 진행했을 때(When), 어떤 상태 변화가 일어난다(Then). +> 이렇게 정리해두면 DisplayName을 더 명확하게 작성할 수 있다. + +
+ +> `settings → gradle → Run tests using : Intellij IDEA`를 통해 테스트 코드를 실행하면 실행명이 `@DisplayName`으로 나온다. + + diff --git "a/Testing/5. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" "b/Testing/3. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" similarity index 99% rename from "Testing/5. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" rename to "Testing/3. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" index 997feed..b904557 100644 --- "a/Testing/5. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" +++ "b/Testing/3. Spring & JPA \352\270\260\353\260\230 \355\205\214\354\212\244\355\212\270.md" @@ -1,4 +1,4 @@ -## Spring & JPA 기반 테스트 +## 3. Spring & JPA 기반 테스트 ### 레이어드 아키텍쳐(Layered Architecture) diff --git a/Testing/3. TDD.md b/Testing/3. TDD.md deleted file mode 100644 index 8c3938f..0000000 --- a/Testing/3. TDD.md +++ /dev/null @@ -1,101 +0,0 @@ -## TDD(Test Driven Development) - -> [TDD 참고](https://github.com/jmxx219/CS-Study/blob/main/etc/TDD.md) - -
- -- 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론 - - 지금까지는 프로덕션 코드를 먼저 만들고 테스트 코드를 작성해왔는데 이 순서를 변경하는 것 -- TDD 개발 주기 - - `RED` : 실패하는 테스트 작성 - - 아직 구현부(프로덕션 코드)가 없기 떄문에 실패함 - - `GREEN` : 테스트를 통과하기 위한 최소한의 코딩 - - 빠른 시일 내에 구현부를 작성하여 통과하기 - - `REFACTOR` : 구현 코드 개선. 테스트 통과 유지 - - 구현 코드 개선 및 테스트 통과 유지 - -
- -### TDD로 테스트 만들기 - -1. 테스트 코드를 먼저 작성한다. - ```Java - class CafeKioskTest{ - ... - @Test - void calclulateTotalPrice(){ - CafeKiosk cafeKiosk = new CafeKiosk(); - Americano americano = new Americano(); - Latte latte = new Latte(); - - cafeKiosk.add(americano); - cafeKiosk.add(latte); - - int totalPrice = cafeKiosk.calculateTotalPrice(); - - assertThat(totalPrice).isEqualTo(8500); - } - } - ``` -2. 메서드가 컴파일 되도록 최소한의 코드만 작성한다. - ```Java - public class CafeKiosk{ - // 1단계: 최소한의 컴파일이 되도록 최소한의 코드 작성 - public int caculateTotalPrice(){ - return 0; - } - } - ``` -3. 빨간불이 뜬다. → `RED` 상태 -4. 빠른 시간 내에 초록불이 뜨게 만든다. → `GREEN` 상태 - ```Java - public class CafeKiosk{ - // 2단계: 빠른 시간 안에 초록불이 뜨게 만들기 - public int caculateTotalPrice(){ - return 8500; - } - } - ``` -5. 리팩토링을 한다. → `REFACTOR` 하기 - ```Java - public class CafeKiosk{ - // 3단계: 리팩토링 하기 - public int caculateTotalPrice(){ - int totalPrice = 0; - for(Beverage beverage : beverage) - totalPrice += beverage.getPrice(); - return totalPrice; - } - } - ``` - - 구현분를 완전히 개편하여도 테스트를 통과한다. → 과감한 리팩토링 가능 - ```Java - public class CafeKiosk{ - public int caculateTotalPrice() { - return beverages.stream().mapToInt(b -> b.getPrice()).sum(); - } - } - ``` - - -
- -#### 선 기능 구현 후, 테스트 작성의 단점 - - 테스트 자체의 누락 가능성 - - 특정 테스트 케이스(해피 케이스)만 검증할 가능성 - - 잘못된 구현을 다소 늦게 발견할 가능성 - - -
- -#### 선 테스트 작성 후, 기능 구현의 장점 - -- 복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다. - - 테스트를 먼저 작성하면, 테스트하기 위한 구조를 고민하게 되어 테스트하기 어려운 영역을 미리 분리할 수 있다. -- 쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다. -- 구현에 대한 빠른 피드백을 받을 수 있다. -- 과감한 리팩토링이 가능해진다. - - -> TDD는 클라이언트 관점에서 우리의 프로덕션 코드를 피드백해주는 Test Driven -> `RED` → `GREEN` → `REFACTOR`를 이용하여 TDD를 구현한다. diff --git "a/Testing/4. \355\205\214\354\212\244\355\212\270\353\212\224 []\353\213\244.md" "b/Testing/4. \355\205\214\354\212\244\355\212\270\353\212\224 []\353\213\244.md" deleted file mode 100644 index d1ba1ec..0000000 --- "a/Testing/4. \355\205\214\354\212\244\355\212\270\353\212\224 []\353\213\244.md" +++ /dev/null @@ -1,53 +0,0 @@ -## 테스트는 [문서]다. - - -### 문서 - -- 프로덕션 기능을 설명하는 테스트 코드 문서 -- 다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완 -- 어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다. - - 테스트 코드가 팀 자산으로 공유할 수 있다는 장점이 존재한다. - -> 우리는 항상 팀으로 일하기 때문에 내가 작성한 코드, 테스트를 다른 팀원이 이해할 수 있도록 문서로 잘 정리해야 한다. - -
- -### DisplayName은 섬세하게 - -> 테스트 함수가 하는 일을 더 상세하게 알려줄 수 있다. - - -- 명사의 나열보다 문장으로 작성하기 - - `~ 테스트`로 끝나는 설명은 지양하자 -- 테스트 **행위**에 대한 **결과**까지 기술하기 -- 도메인 용어를 사용하여 한 층 추상화된 내용을 담기 - - 메서드 자체의 관점보다 도메인 정책 관점으로 작성하기 - - ex) `우리 카페 키오스크의 영업 시작 시간`이라는 도메인 용어를 사용하기 -- 테스트의 현상을 중점으로 기술하지 말 것 - -
- -### BDD(Behavior Driven Development) 스타일로 작성하기 - -- TDD에서 파생된 개발 방법 -- 함수 단위의 테스트에 집중하기보다 **시나리오에 기반한 테스트케이스(TC)** 자체에 집중하여 테스트한다. -- 개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨)을 권장한다. - -
- -#### Given / When / Then - -- Given : 시나리오 진행에 필요한 모든 준비 과정(객체, 값, 상태 등) -- When: 시나리오 행동 진행 -- Then: 시나리오 진행에 대한 결과 명시, 검증 - -> 어떤 환경에서(Given) 어떤 행동을 진행했을 때(When), 어떤 상태 변화가 일어난다(Then). -> 이렇게 정리해두면 DisplayName을 더 명확하게 작성할 수 있다. - -
- -> `settings → gradle → Run tests using : Intellij IDEA`를 통해 테스트 코드를 실행하면 실행명이 `@DisplayName`으로 나온다. - - - -