728x90
반응형
TDD 법칙 세 가지
- 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
- 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
- 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
깨끗한 테스트 코드 유지하기
테스트 코드는 실제 코드 못지 않게 중요하며, 실제 코드 못지 않게 깨끗하게 짜야함
테스트는 유연성, 유지보수성, 재사용성을 제공한다
코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목은 단위테스트
테스트 케이스가 있으면 코드 구조 변경이 쉬워짐
테스트 코드가 지저분하면 코드를 변경하는 능력이 떨어지며 코드 구조를 개선하는 능력도 떨어짐
깨끗한 테스트 코드
가독성이 중요하다!
// before
public void testGetPageHieratchyAsXml() throws Exception {
crawler.addPage(root, PathParser.parse("PageOne"));
crawler.addPage(root, PathParser.parse("pageOne.ChildOne"));
crawler.addPage(root, PathParser.parse("PageTwo"));
request.setResource("root") ;
request.addlnput("type" , "pages");
Responder responder = new SerializedPageResponder();
SimpleResponse response =
(SimpleResponse) responder.makeResponse(
new FitNesseContext(root) , request);
String xml = response.getContent();
assertEquals("text/xml", response. getContentType ( ) ) ;
assertSubString("<name>PageOne</name>", xml);
assertSubString("<name>PageTwo</name>", xml) ;
assertSubString("<name>ChildOne</name>", xml);
}
// after
public void testGetPageHierarchyAsXml() throws Exception {
makepages("pageOne", "pageOne. Chi ldOne", "pageTI 0");
submitRequest("root", "type:pages");
assertResponseIsXML();
assertResponseContains(
"<name>PageOne</name>" , "<name>PageTwo</name>'’ , "<name>ChildOne</name>"
);
}
테스트 자료를 만드는 부분, 테스트 자료를 조작하는 부분, 조작한 결과가 올바른지 확인하는 세 부분으로 나뉘어짐
잡다하고 세세한 코드를 없애 코드가 수행하는 기능을 재빨리 이해하게 만들어줌
도메인에 특화된 테스트 언어
시스템 조작 API를 사용하는 대신 테스트 코드에서 사용하는 특수 API를 만들어서 사용
처음부터 설계된 API가 아니므로 코드를 계속해서 리팩터링 하다가 진화시킬 필요가 있음
이중 표준
테스트 API코드에 적용하는 표준은 실제 코드에 적용하는 표준과 다름
단순하고, 간결하고, 표현력이 풍부해야 하지만 실제 코드만큼 효율적일 필요는 없음(ex StringBuffer)
테스트 당 assert 하나
assert 문이 단 하나인 함수는 결론이 하나라서 코드를 이해하기 쉽고 빠름
given-when-then 에서 given/when 부분이 지나치게 중복되거나 @Before함수에 넣는 등 가독성이 더 떨어질 수 있으므로 assert문 개수는 최대한 줄이는 것이 좋지만 필요에 따라 여러개를 쓰는 것도 좋은 방법
테스트 당 개념 하나
잡다한 개념을 연속으로 테스트하는 긴 함수는 피해야함
public void testAddMonths() {
SerialDate d1 = SerialDate.createInstance(31, 5, 2004);
// 31일에 끝나는 5월에 한달을 더하면 30일로 끝나지 체크
SerialDate d2 = SerialDate.addMonths(1, d1);
assertEquals(30, d2.getDayOfMonth());
assertEquals(6, d2.getMonth());
assertEquals(2004, d2.getYYYY());
// 두달을 더한 7월은 31일로 끝나는 달
SerialDate d3 = SerialDate.addMonths(2, d1);
assertEquals(31, d3.getDayOfMonth());
assertEquals(7, d3.getMonth());
assertEquals(2004, d3.getYYYY());
// 30일로 끝나는 달에 한달을 더하면 30일이 되어야 됨
SerialDate d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1));
assertEquals(30, d4.getDayOfMonth());
assertEquals(7, d4.getMonth());
assertEquals(2004, d4.getYYYY());
}
F.I.R.S.T
깨끗한 테스트를 위한 다섯 가지 규칙
- 빠르게(Fast)
- 자주 돌리지 않으면 초반에 문제를 찾아내 고치지 못하므로 테스트는 빨라야 하며 빨리 돌아야됨
- 독립적으로(Independent)
- 각 테스트는 서로 의존하면 안됨
- 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안됨
- 어떤 순서로 실행해도 괜찮아야됨
- 반복가능하게(Repeatable)
- 어떤 환경에서도 반복 가능해야됨
- 실제 환경, QA 환경, 네트워크에 연결되지 않은 환경에서도 실행할 수 있어야 됨
- 자가검증하는(Selv-Validationg)
- 테스트는 부울 값으로 결과를 내야됨(성공 아니면 실패)
- 통과 여부를 알려고 로그 파일을 읽게 만들어서는 안됨
- 적시에(Timely)
- 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현
- 실제 코드를 구현한 다음에 테스트 코드를 만들면 테스트가 어렵거나 불가능하게 실제 코드를 설계할지도 모름
GPT) 단위테스트 시 검증해야될 것들
- 기능적 요구사항 검증
- 주요 동작 확인: 메서드나 함수가 기대한 대로 작동하는지 확인합니다.
- 정확성 검증: 입력값에 대해 예상한 출력값이 반환되는지 확인합니다. 예: add(2, 3) → 5
- 경계값 검증: 최소값, 최대값, 또는 경계값을 입력으로 주었을 때 올바르게 처리되는지 확인합니다.
예: 배열 인덱스가 범위를 초과하지 않는지.
- 비정상 상황 및 예외 처리
- 예외 처리 검증: 비정상적인 입력이나 상황에서 올바른 에러 또는 예외가 발생하는지 확인합니다.
예: null 값이나 잘못된 데이터 형식 처리. - 안정성 검증: 프로그램이 중단되지 않고 적절히 예외를 처리하는지 확인합니다.
- 예외 처리 검증: 비정상적인 입력이나 상황에서 올바른 에러 또는 예외가 발생하는지 확인합니다.
- 상태 변화 검증
- 상태 관리: 함수나 메서드가 호출된 후 내부 상태나 객체의 상태가 기대한 대로 변화했는지 확인합니다.
예: 객체의 프로퍼티 값이 예상대로 업데이트되었는지.
- 상태 관리: 함수나 메서드가 호출된 후 내부 상태나 객체의 상태가 기대한 대로 변화했는지 확인합니다.
- 외부 종속성 검증
- API 호출: 외부 API 또는 데이터베이스 호출이 올바르게 이루어졌는지 확인합니다. (Mocking 활용)
- 의존성 주입: 의존성이 올바르게 동작하고 테스트 가능한 구조인지 확인합니다.
- 성능 및 시간 관련 테스트
- 시간 민감도 테스트: 특정 시간 내에 실행이 완료되거나, 타이밍이 중요한 로직(예: Debounce)이 기대대로 동작하는지 확인합니다.
- 코드 유지보수성 관련
- 코드 분리: 함수가 단일 책임 원칙(SRP)을 지켜 독립적으로 테스트 가능하게 설계되었는지 확인합니다.
- 테스트 이름: 테스트 케이스의 이름을 통해 기능과 검증 항목을 명확히 알 수 있도록 작성합니다.
- UI 테스트 (프론트엔드)
- 렌더링 검증: 컴포넌트가 올바르게 렌더링되는지 확인합니다.
- 이벤트 처리: 사용자 입력(클릭, 드래그 등)에 대해 올바르게 반응하는지 확인합니다.
- 상태 변화: UI 상태가 예상대로 변경되었는지 확인합니다.
- 접근성: WCAG 기준에 부합하는지 확인합니다.
- 회귀 테스트
- 이전에 발견된 버그가 수정된 후 다시 발생하지 않는지 확인합니다.
- 변경 사항이 기존 기능에 영향을 미치지 않았는지 확인합니다.
- 테스트 커버리지
- 경로 커버리지: 가능한 모든 코드 경로를 테스트했는지 확인합니다.
- 조건 커버리지: 모든 조건(참/거짓)을 개별적으로 테스트했는지 확인합니다.
- 코드 커버리지 도구 활용: Jest, Istanbul, Jacoco 등의 도구로 테스트 커버리지를 분석합니다.
- Best Practices
- AAA 패턴: Arrange(준비), Act(실행), Assert(검증) 구조를 유지합니다.
- 독립적 테스트: 테스트는 서로 독립적이어야 하며, 실행 순서에 영향을 받지 않아야 합니다.
- 간결성: 한 테스트에서 하나의 기능이나 요구사항만 검증합니다.
- Mocking/Stubbing: 외부 의존성을 최소화하여 테스트의 순수성을 유지합니다.
728x90
반응형
'독후감' 카테고리의 다른 글
[실전 코드로 배우는 Vue.js] Chapter 11. vue 테스트 (0) | 2024.11.17 |
---|---|
[실전 코드로 배우는 Vue.js] Chapter 6. 외부 데이터 통합 (0) | 2024.11.12 |
[실전 코드로 배우는 Vue.js] Chapter 2. Vue의 기본 작동 방식 정리 (1) | 2024.11.11 |