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) 개방-폐쇄 원칙

ttd2

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

ttd3

3. 입력과 출력

입력 > 논리 > 출력

  • 직접 입력과 직접 출력
    • 공개된 인터페이스를 통한 입력과 출력
    • 다루기 간단함
  • 간접 입력과 간접 출력
    • 입력된 인터페이스를 통한 입력과 출력
    • 다루기 복잡함

부작용

  • 인터페이스 설계에 드러나지 않은 출력
    • 반한 값 외 출력
  • 자주 사용되는 부작용
    • 실패
    • 지연
    • 간접출력

4. 테스트 대역

대역과 가정

  • DOC 준비 비용이 큰 경우
    • 구동에 많은 자원이 필요
    • 환경 제어가 어려움
  • DOC가 SUT에 제공하는 인터페이스를 준수하는 대역 코드를 사용
  • 대역 코드가 계약을 DOC와 동일하게 준수할 것이라고 가정(assyme)

Dummy

SUT 준비를 위해 해결되어야 하는 의존성이 테스트 대상 논리에 의해 사용되지 않는 경우에 의존 요소를 대신하는 테스트 대역

if...
	logic --> 테스트 대상
else
	logic using dependency

Stub

stub

  • 간접 입력 대역
  • 미리 준비된 답을 출력

Mock

  • SUT 내부의 행위(상호작용) 검증

… 추가하기

5. Mockists vs Classicists

Sociable 테스트 vs Solitary 테스트

단위 테스트 -> 시스템 -> 의존대상

  • ex) 외부 의존성까지 같이 테스트

Solitary 테스트

단위 테스트 -> 시스템 -> 테스트 대역

  • 시스템 내부만 테스트

  • 처음에는 Solitary 테스트만 해야한다고 생각한다.

가정의 안정도

테스트 대역 사용으로 인해 생기는 가정의 얼마나 믿을 수 있을까?

  • 테스트 대역이 구현하는 인터페이스가 단순할 수록 안정적임
  • 테스트 대역이 구현하는 인터페이스가 복잡할 수록 불안정함

Mock의 위험

  • 상태 검증 vs 행위 검증
    • 상태 검증: 인터페이스에 입력을 주고 어떤 출력이 나오는지 테스트 하는 검증
    • 행위 검증 : 어떻게 출력을 만들어내는지 검증하는 방법
  • 테스트가 SUT 구현에 의존
  • 고통스럽고 불안한 리팩터링
    • mock은 내부 구현과 관련있기 때문에 리팩터링을 하게 되면 테스트가 깨진다.

Comments

comments powered by Disqus