TDD에 대해서2
2부. 테스트 주도 개발의 깊은 곳
1. 인터페이스와 구현
추상화
“The essence of abstraction is preserving information that is relevant in a given context, and forgetting information that is irrelevant in that context.”
-John Vogel Guttag
- 목적에 따라서 대상이 가지고 있는 일부의 특징만 투용하는 것
- 투영된 결과가 모델이다.
협력과 계약
- 대부분의 코드는 다른 코드와 협력
- 협력에 필요한 것은 ‘어떻게’가 아닌 ‘무엇’
- 무엇을 하는지가 중요하지 어떻게 구현되어 있는지는 중요하지 않음.
- 인터페이스
- ‘무엇을 표현’
- 접점을 표현한 명세서
- 클라이언트 코드에게 반드시 필요한 정보
- 협력하는 코드 사이의 계약
- 추상화 결과
인터페이스에 프로그래밍
“We program against an interface. We’re programming against a thing without regard to theire content.”
-Erik Meijer
즉, 어떠한 코드를 사용할 때, 사용법만 알면되지, 구현에 대해 알아야 할 필요는 없다.
정보 숨김
- 1971 David Lorge Parnas 가 논문에서 소개함
- 효과적인 모듈화란?
- 조직 간 의사소통 최소화
- 변경 여파 최소화
- 시스템 이해 도움
- 공개된 설계 결정과 숨겨진 설계 결정
- 어려 설계 결정과 변경될 것 같은 설계 결정을 숨겨라.
2. 환경 변화와 적응력

- 시스템 A는 환경 변화가 있을 때 마다 모든 코드를 수정해야 함
- 시스템 B는 환경 변화가 있을 때 일부만 수정하게 됨
- 모듈화가 잘 되어 있고
- 분리가 잘 되어있고
- 확장하기 쉬움
내가 짠 코드를 환경 변화에 적응할 수 있도록 작성하자.
코드는 수명이 다할 때까지 많은 변화를 겪게 됩니다. 이런 변화에 경제적으로 대응하는 코드를 설계하는 방법에 대한 기법들을 알아봅니다.
적응력
- 다른 기능을 추가할 때마다 기존 코드의 변경을 최소화 하는 방법이 좋은 코드 작성법 입니다.
객체 지향 (Object-oriented)
“OOP to me means only messaging, local retention and protection and hiding of state-process,and extreme late-binding of all things”
Alan Kay
late-bingding(정적 바인딩)
- 다형성 (Polymorphism)
- 여러가지 형태를 가질 수 있는 능력
- 일반성 (Genericity)
개방-폐쇄 원칙 (Open-closed principle)
소프트웨어 엔터티(클래스, 모듈, 함수 등) 는 확장에 열리고 수정에 닫혀야 한다.
Bertand Meyer
- 확장 가능한 경우 모듈은 열려 있다고 말한다.
- 코드를 수정하지 않고 확장 가능해야함.
- 다른 모듈에 사용될 수 있을 때 모듈은 닫혀 있다고 말한다.
- 한 모듈이 다른 모듈에 사용될 때 원래의 모듈이 빌드된 상태에서 제공되어야 한다.
- 상속을 염두 한 정의
다형적(Polymorphic) 개방-폐쇄 원칙

- 모듈이 이용하는 Abstraction에 따라, 코드 변경없이 기능이 변경되거나 확장될 수 있다.
플러그인 (Composite 패턴)

3. 입력과 출력
입력 > 논리 > 출력
- 직접 입력과 직접 출력
- 공개된 인터페이스를 통한 입력과 출력
- 다루기 간단함
- 간접 입력과 간접 출력
- 입력된 인터페이스를 통한 입력과 출력
- 다루기 복잡함
부작용
- 인터페이스 설계에 드러나지 않은 출력
- 반한 값 외 출력
- 자주 사용되는 부작용
- 실패
- 지연
- 간접출력
4. 테스트 대역
대역과 가정
- DOC 준비 비용이 큰 경우
- 구동에 많은 자원이 필요
- 환경 제어가 어려움
- DOC가 SUT에 제공하는 인터페이스를 준수하는 대역 코드를 사용
- 대역 코드가 계약을 DOC와 동일하게 준수할 것이라고 가정(assyme)
Dummy
SUT 준비를 위해 해결되어야 하는 의존성이 테스트 대상 논리에 의해 사용되지 않는 경우에 의존 요소를 대신하는 테스트 대역
if...
logic --> 테스트 대상
else
logic using dependency
Stub

- 간접 입력 대역
- 미리 준비된 답을 출력
Mock
- SUT 내부의 행위(상호작용) 검증
… 추가하기
5. Mockists vs Classicists
Sociable 테스트 vs Solitary 테스트
단위 테스트 -> 시스템 -> 의존대상
- ex) 외부 의존성까지 같이 테스트
Solitary 테스트
단위 테스트 -> 시스템 -> 테스트 대역
-
시스템 내부만 테스트
-
처음에는 Solitary 테스트만 해야한다고 생각한다.
가정의 안정도
테스트 대역 사용으로 인해 생기는 가정의 얼마나 믿을 수 있을까?
- 테스트 대역이 구현하는 인터페이스가 단순할 수록 안정적임
- 테스트 대역이 구현하는 인터페이스가 복잡할 수록 불안정함
Mock의 위험
- 상태 검증 vs 행위 검증
- 상태 검증: 인터페이스에 입력을 주고 어떤 출력이 나오는지 테스트 하는 검증
- 행위 검증 : 어떻게 출력을 만들어내는지 검증하는 방법
- 테스트가 SUT 구현에 의존
- 고통스럽고 불안한 리팩터링
- mock은 내부 구현과 관련있기 때문에 리팩터링을 하게 되면 테스트가 깨진다.
Comments