# 2부. 테스트 주도 개발의 깊은 곳 ###### tags: `지호`, `이규원의 현실 세상의 TDD : 안정감을 주는 코드 작성 방법` --- ## 1. 인터페이스와 구현 ### 협력과 계약 - 대부분의 코드는 다른 코드와 협력 - 협렉에 필요한 것은 '어떻게'가 아닌 '무엇' - 인터페이스 - '무엇'을 표현 - 클라이언트 코드에게 반드시 필요한 정보 - 협력하는 코드 사이의 계약 - 추상화 결과 ### 정보 숨김 - 효과적인 모듈화 - 조직 간 의사소통 최소화 - 변경 여파 최소화 - 시스템 이해 도움 - 공개된 설계 결정과 숨겨진 설계 결정 - 어려운 설계 결정과 변경될 것 같은 설계 결정을 숨겨라 <br/> ## 2. 환경 변화와 적응령 ### 개체 지향 (Object-oriented) - 지연된 바인딩 - 다형성 - 일반성 ### 개방-폐쇄 원칙 (Open-closed principle) - 소프트웨어 엔터티(클래스, 모듈, 함수 등)는 확장에 열리고 수정에 닫혀야 한다. - 상속을 염두에 둔 정의 <br/> ## 3. 입력과 출력 ### 입력과 출력 - 입력 -> 논리 -> 출력 - 직접 입려과 직접 출력 - 공개된 인터페이스를 통한 입력과 출력 - 다루기 간단함 - 간접 입력과 간접 출력 - 입력된 인터페이스를 통한 입력과 출력 - 다루기 복잡함 ### 부작용 - 인터페이스 설계에 드러나지 않은 출력: 반환 값 외 출력 - 자주 사용되는 부작용: 실패, 지연, 간접 출력 <br/> ## 4. 테스트 대역 ### 대역과 가정 - DOC 준비 비용이 큰 경우 - 구동에 많은 자원이 필요 - 환경 제어가 어려움 - DOC가 SUT에 제공하는 계약(인터페이스)을 준수하는 대역 코드를 사용 - 대역 코드가 계약을 DOC와 동일하게 준수할 것이라고 가정 ### Dummy - SUT 준비를 위해 해결되어야 하는 의존성이 테스트 대상 논리에 의해 사용되지 않는 경우에 의존 요소를 대신하는 테스트 대역 ### Stub - 간접 입력 대역 - 미리 준비된 답을 출력 ### Spy - 간접 출력 대역 - SUT의 간접 출력을 기록 ### Mock - SUT내부의 행위(상호작용) 검증 ### Facke - 의존성 계약을 준수하는 가벼운 구현체 - DOC보다 적은 부작용 - 인메모리 데이터베이스 등 <br/> ## 5. Mockists vs Classicists ### Sociable 테스트 - 단위 테스트 -> 시스템 -> 의존 대상 ### Solitary 테스트 - 단위 테스트 -> 시스템 -> 테스트 대역 ### Mock의 위험 - 상태 검증 vs 행위 검증 - 정보 숨김 위배 - 테스트가 SUT 구현에 의존 - 고통스럽고 불안한 리팩토링 <br/> ## 6. Should I test private methods ### 비공개 모듈 테스트 - 비공개 모듈의 작성과 사용은 공개 모듈의 구현 영역 - 비공개 모듈 테스트는 공개 모듈 구현 노출 - 테스트가 공개 모듈 구현에 의존 - 정보 숨김 위배 - SUT와 테스트 사이의 높은 결합 <br/> ## 7. 테스트 주도 설계 ### 설계가 단위 테스트에 미치는 영향 - 테스트는 인터페이스 설계에 의존 - 인터페이스 설계 품질이 낮으면 테스트 작성이 불편함 ### 단위 테스트가 설계에 미치는 영향 - 테스트가 있기 때문에 리팩토링 가능 - 두려움 없이 구현 설계를 과감하게 개선 ### 단위 테스트에 의지하는 인터페이스 설계 - 단위테스트 - 낮은 응집에 대한 피드백을 주지 않는다. - 일관된 설계를 강요하지 않는다. - 의도 노출을 요구하지 않는다. ### 단위 테스트에 의자하는 구현 설계 - 단위 테스트는 책임 분산을 유도하지 않는다. - Mockists - 비공개 운영 코드 테스트 <br/> ## 8. 테스트 주도 개발의 한계 ### 은탄환은 없다 - 테스트 주도 개발은 유용하고 매력적인 도구 - 하지만 테스트 주도 개발 역시 남용을 주의해야 한다. ### 불안정한 목표 - 모든 코드의 목표가 안정적이지 않음 - 탐색적으로 목표를 찾아내야 한다면 테스트 코드 작성 비용 부담이 큼 ### 환경 제어 - 하위 시스템이 협력하는 다른 하위 시스템, 의존성 중 일부는 출력의 예측과 제어가 어려움 - 단위 테스트는 결정적일수록 유용 - 의존성 중 일부는 동작이 매우 느림 - 단위 테스트는 빠를수록 유용 - 의존성의 단점을 보완할 대역을 도입하면 가정이 발생 - 가정으로 인한 안정감 감소 ### 낮은 코드 적응력 - 코드 적응령이 낮으면 단위 테스트하기가 매우 어렵다. - 이미 코드 적응력이 낮은 코드 기반에 테스트 주도 개발을 적용하기 어렵다. - 코드 기반의 적응력을 높이는 것도 어렵다. - 설계 변경의 어려움 - 변경된 설계 검증 필요 <br/> ## 9. 인터페이스와 테스트 ### 인터페이스 - 한 개체가 상호작용하는 다른 개체에 제공하는 상호작용 지점 ### API - Application programming interface - 한 시스템이 협력 시스템에 제공 - 코드 친화적 소통 수단 - 테스트 자동화 비용이 낮음 ### UI - User interface - 한 시스템이 시스템 사용자에게 제공 - 인간 친화적 소통 수단 - 변경이 잦음 - 테스트 자동화 비용이 높음 <br/> ## 10. 인수 테스트 주도 개발 ### 인수 테스트 - 배치된 코드를 대상으로 최종 클라이언트 관점으로 테스트 - UI 응용프로그램 - 사용자가 클라이언트 - 테스트 자동화 작성고 운영 비용이 상대적으로 높음 - API 응용프로그램 - 외부 시스템이 클라이언트 - 테스트 자동화 작성과 운영 비용이 상대적으로 낮음 <br/> ## 11. 코딩 계획 ### 코딩 작업 설계 - 목표를 명확히 기술 - 작업 분리 ### 업무 가시성 - 투명한 작업자의 업무 내용과 진행도 - 위험 요소를 더 빨리 발견할 가능성이 높아짐