# 각종 테스트 지원 라이브러리
## DBUnit
1. DBUnit 의 데이터셋을 활용하여 손쉽게 DB테스트를 할 수 있다.
2. xml파일을 읽어들여서 테스트 데이터를 만들고 원하는 로직을 검증한다.
3. Assertion 을 제공한다.
4. but 이러한 설정파일을 만들고 테스트하는데 걸리는 시간이 필요하므로, 반드시 DBUnit과 같은 라이브러리를 사용해야만하는지에 대해서 생각해보자.
## Unitils
1. 리플렉션 단정문을 이용해서 동등성비교가 가능하다.
- 동일성과 동등성
- 동일성 : ==로 비교하는것. 수학적 연산.
- 동등성: 논리적인 같음을 포함한다. equals 비교. 객체를 비교할 때는 기본적으로 동등성이 보장되지 않음. 동등성비교해서 같게나오게 하고싶으면. equalsAndHashcode를 같게 만들어주어야 한다. 같은 객체는 같은 메모리 주소를 가져야 한다.
- 참고: 해쉬코드 연산시 : set등에 저장할때 이것을 같게 만들어주지 않으면 겉보기 내용물은 같아도 두개의 객체가 저장된다.
- Hash알고리즘을 구현하는 컬렉션은 - 어떠한 그들만의 해쉬함수연산을 돌려서 저장한다. set이 데이터의 순서를 보장하지 않는 것은 맞지만, 뭔가 비슷비슷한 순서로 저장되는 것은 이때문이다. 같은 해시함수를 사용하기 때문에 일정 기준의 객체는 비슷비슷한 위치에 저장되는 것이다.
2. assertPropertyLenientEquals를 사용해서 자바리플렉션 사용하는 효과 비슷한 것을 누릴 수 있다. 아니면 그냥 reflection 써서 컨스트럭터 만들고 프로퍼티 불러와도 된다.
3. Transaction phase 를 지정할 수 있다.
4.@Runwith으로 테스트 러너를 확장할 수 있다.
5. @Before에서 관련 라이브러리 및 데이터셋을 셋팅하고, @After에서 tearDown으로 데이터 커넥션을 close하는 메소드를 부를 수 있다.
# TDD 작성 패턴
## 일반적인 어플리케이션
- 생성자 작성패턴 : 잘 사용하지 않지만 db커넥션등을 확인하거나 할때 사용하기도 함.
- dto스타일의 객체 테스트 : 불변객체를 테스트 할때 사용하기도 함.
- 불변객체란 ? : 한번 값이 정해지면, 변하지 않는 클래스. 대표적으로 String클래스가 있다. String은 부를 때마다 new String("김마리"); 같이 새로 메모리가 할당된다.
이러한 객체를 만들어줄 필요성이 있을떄가 있다.. 멀티스레드 프로그래밍 할때라든지.
- 닭과 달걀 : a 메소드를 테스트하기 위해 b 메소드가 필요한 경우
- 때에 따라서는 .getDeclaredField 같은 메소드 (리플렉션) 사용해서 가져오기도 하지만. 보기에 코드가 길어지고 그다지 좋지않은 방법. 굳이굳이 해당 접근제한자 바꾸면서 - (setAccessible(true))접근하는것은 권장되지 않는다.
- 배열테스트 : assertArrayEquals사용
- Collection테스트 : 굳이 equals and hashcode를 구현하기 싫다면(?) toString을 비교하는 방식으로 우회할 수 있다. 정렬할경우 sort같은 메소드 사용하거나. 아니면 소트없이 그냥 비교하는 방식사용.
## 웹 어플리케이션
- 각 계층별 테스트가 필요할 수 있다.
- 프레젠테이션 영역, 어플리케이션 영역, 도메인로직 영역, 인프라스트럭쳐 영역으로 각각 단위 테스트 및 통합테스트가 필요할 수 있다.
- 특히 도메인 로직을 담당하는 부분은 반드시 테스트가 필요하다고 볼 수 있을 것이다.
- 프레젠테이션 영역: 사용자와의 인터렉션. 상호작용 담당. 파라메터를 받는다. 흔히 생각하는 컨트롤러.
- 어플리케이션 영역: 수행해야하는 로직의 뼈대를 잡는다. 말하자면 지휘관.
- 도메인로직 영역 : 어플리케이션의 핵심로직이 들어가는 부분. 변경의 생명주기에 따라 쪼개어질수있다.
- 인프라스트럭쳐 영역 : 로직을 구현하기 위한 구체적인 구현 객체들이 들어가게 된다. 변경사항에 따라서 얼마든지 바뀔 수있어야함.
### 뷰 테스트
- 반드시 해야하는가? 고민해볼 수 있다.
- 셀레늄과 같은 라이브러리를 이용할 수 있다.
- 사용자 화면(클라이언트) 를 테스트한다는 것인지, 서버 화면을 테스트한다는 것인지 명확해야한다.
### 컨트롤러 테스트
- 스프링 프레임워크의 목객체를 이용해서 MockMVC를 만들고 파라메터를 검증할수있다.
### 서비스 테스트
- 모델에 대한 TDD는 최대한으로 적용한다.
## DB테스트
- 트랜잭션 처리 : 끝나면 롤백한다.
- before, after이용해서 delete 및 insert 한다.
- sql스크립트 파일을 하나 작성해서. 자동적으로 수행되게끔한다
## 테스트 코드의 리팩토링
### 테스트 코드의 원칙
- 테스트 코드는 각각이 독립적으로 수행되어야 한다. 하나의 테스트코드가 다른 테스트 코드에 영향을 미치면 안된다. 이를테면 a테스트코드가 먼저 수행되어야만 성공하는 b테스트코드는 잘못된것이다(하지만 물론 절대적인것은 아니고, 문맥에 따라서는 이러한 테스트코드가 필요한 상황이 있을수있다.)
- 일관된 시나리오를 가지고 있어야한다. 1번과 이어진다. 하나의 테스트코드는 독립적이기 때문에, 한개 테스트코드 내부의 시나리오에 완결성이 있어야한다. -> but 이것역시 절대적인 것은 아니다. 중복을 제거하기 위해서 다른 메소드를 만들어 호출할 수 도 있는것이다.
# TDD에 대한 다양한 시각
## 높은 응집도, 낮은 결합도
- 테스트코드를 작성하다보면, 의존성이 많으면 많을수록 테스트가 어렵다는 것을 깨닫게 된다. 자연스럽게 결합도가 낮은 클래스를 만들수있게 된다.
## 계약에 의한 프로그래밍
- 계약이란(Contract) : 고객의 요구사항, 업무 규칙, 제한조건 등 모든것을 포함한다.
- 테스트주도개발은 방어형 코딩을 주도해준다. 좀더 오류를 줄일 수 있는 것이다.
## TDD유의사항
- 테스트 케이스는 이름이 중요하다. 자신만 알아볼 수 있는 테스트 케이스는 효용성이 떨어진다. 다른 동료에게 읽혀도 명확하도록 작성해야 한다.
- 제대로 동작하지않는 테스트 케이스는 과감하게 제거한다. 우리에게는 버젼관리 툴이 있으니까 지운다고 해서 문제될것은 없을 것이다.
- 현재 필요한 기능에 대한 테스트케이스만 만든다. 모든 상황을 가정하고 예상하면서 테스트 케이스를 만들면 지쳐 떨어져나가게 될것이다.
- 테스트 케이스 독립성을 유지한다. 목객체를 이용해서 최대한 의존성을 낮추는 것을 고려해본다. db연결등.
## 행위주도개발 (BDD)
- 어플리케이션의 행위 . 서비스 중에서. 가치있는 기능. 꼭 필요한 기능부터 개발한다
- 탑다운 방식이라고 볼 수 있다
- 흔히 생각하는 - 실패하는 가장 간단한 테스트 코드부터 작성하라 - 라는 원칙은 다운탑 방식이다. 알고리즘 작성에는 효율적이나 실제 기능개발에 있어서는 사용되지않는 메소드까지 전부다 구현해야 하기 때문에 현실적인 상황에 맞지않을 수 있다.
- 런던학파 vs 시카고 학파.
- 사용자 중심으로 생각하고 , 중요한 기능중심으로 테스트를 작성한다.
- given / when / then템플릿을 이용할 수 있다.
## TDD 실제사용
- 업무시나리오 생각 -> 사람이 하는 부분 제외 -> 하드웨어가 하는 부분 제외 -> 요구사항 정련
- enum 사용고려
- 매직넘버 -> static변수로 바꾸는 것고려. 명확하게 의미를 구분한다
- 변수명 및 메소드 명을 정돈한다
- assert하는 메소드 명을 추출, 이름을 붙여서 명확하게 하는 것을 고려한다
```
- ‘업무규칙’과 ‘기능 요구사항’을 분리한다.
- ‘고유명사는 대명사로, 명시적인 숫자는 변수로’ 참조할 수 있도록 표시해두면 도움이 된다.
이때 종류 중 하나를 나타낼 때는 어떤(any)으로 치환해 표현하는 것도 때로 도움이 된다.
- 행동의 주체를 가급적 명확히 표현한다.
- 동작과 소유를 분리한다.
예: 객체의 동작인가? 객체의 소유인가?
- 복합 한자어는 유심히 봐서 분리 가능한지 확인해서 풀어쓰면 더 좋다.
예: 대여요금 → 대여행위 시에 발생하는 요금
- 상호작용을 유심히 관찰한다.
예: 시스템의 행위인가? 고객의 행위인가?
- 구어체는 좀 더 명확한 의미를 가진 문어체로 변경한다.
예: 구한다 → 화면에 보여준다. 계산한다. 저장한다
올라간다 → 증가한다. 누적된다.
```
- 업무규칙(우리 회사의 룰), 요구사항(고객과 협의해서 정해야 하는룰) 등등 확인
## 테스트 자동화
- 빌드도구를 사용하여 테스트 케이스 자동화를 할 수 있다.
- 수행결과를 보고서로 확인할 수 있다.
## 테스트커버리지
- 커버리지 퍼센트가 중요한 것이아니라, 내포된 업무조건을 파악하는 것이 더 중요.