본문 바로가기

IT Book Summary/TDD

3부 테스트 주도 개발의 패턴. 25장- 28장

25장. 테스트 주도 개발 패턴

 

  • 테스트 한다는 것은 무엇을 뜻하는가?
  • 테스트를 언제 해야 하는가?
  • 테스트할 로직을 어떻게 고를 것인가?
  • 테스트할 데이터를 어떻게 고를 것인가?

테스트 (명사)

작성한 소프트웨어를 어떻게 테스트할 것인가? - 자동화된 테스트를 만들어라

 

test : 평가하다.

: 승인 또는 거부에 도달하는 과정.

 

'테스트할 시간이 없다' 의 죽음의 나선. positive feedback loop

스트레스를 많이 받으면 테스트를 덜하게됨 -  테스트를 덜하면 에러가 많아짐 

                                   \                                                /

                                  에러가 많아지면 스트레스를 더 받음

 

어떻게 이 악순환에서 벗어날 수 있을까?

-> '테스트'를 '자동화된 테스트'로 치환하면 된다.

자동화된 테스트가 있다면 스트레스를 받기 시작할때 테스트부터 실행할 것이다.

그리고 테스트 함으로써 에러도 줄게된다. 때문에 스트레스도 줄게될 것이다. 

 

격리된 테스트

테스크를 실행한다고 해서 다른것에 영향이 있으면 안된다.

 

테스트는 전체 애플리케이션을 대상으로 하기보다 작은 스케일로 하는게 좋다.

그리고 각각의 테스트는 서로 독립적이어야 한다.

하나의 문제에 하나의 테스트가 실패하도록 해야함.

테스트를 격리하기 위한 작업은 결과적으로 응집도는 높고 결합도는 낮은 객체의 모음으로 구성되도록 한다.

 

테스트 목록

무엇을 테스트 해야하나? -> 시작하기전 할일 테스트 목록을 적어둘 것.

성취하고자하는 목표가 분명해야함.

 

하지만 할일목록이 많아지면 성취도는 낮아지고, 성취도가 낮아지면 할일목록은 많아진다.

구현해야할 것들에 대한 테스트 목록

모든 오퍼레이션의 사용예

해당 오퍼레이션에 널 버전 리스트

리팩토링목록

을 모두 적는다.

 

테스트를 한번에 다 만들어 놓는방법이 별로인 이유

- 한번 만들어진 모든 테스트는 리팩토링하게 되지 않음.

- 열개의 실패한 테스트로는 초록막대를 보기 힘들어지게 되므로 

 

테스트가 통과하는과정에서 만들어지는 코드가 새로운 테스트로 자연스레 이끈다. 

 

테스트 우선

테스트를 언제 작성하는 것이 좋을까?

-> 테스트 대상이 되는 코드를 작성하기 직전.

 

코드를 작성하면 테스트를 만들지 않을것이기 때문에 

설계와 작업범위 조절을 생각할 수도 있다.

 

스트레스 받기전에 먼저 테스트 해버리자.

 

단언 우선

테스트시 단언은 언제 쓸까? -> 제일먼저.

  • 시스템 개발할 대 무슨일 부터 하는가? 완료된 시스템이 어떨거라고 알려주는 것부터 작성
  • 특정 기능을 개발할 때 무슨일 부터 하는가? 기능이 완료되면 통과할수 있는 테스트 작성
  • 테스트를 개발할 때 무슨일 부터 하는가? 완료될 때 통과해야 할 단언부터 작성.

단언을 먼저 작성하면 작업을 단순하게 만든다.

 

- 테스트하고자 하는 기능이 어디에 속하는지

- 메서드 이름은 어떻게 하는지

- 올바른결과를 어떤식으로 검사하는지

- 이 테스트가 제안하는 또 다른테스트는 무엇인지

 

아주 작은 단계로 빠른 피드백을 받으며 테스트의 아웃라인을 만들수 있음.

 

테스트 데이터

테스트시 어떤 데이터를 사용해야 하는가? -> 읽을 때 쉽고 따라가기 좋을 데이터를 사용하라.

 

적은 항목으로 동일한 설계와 구현을 이끌수 있다면 구지 여러개를 사용할 필요가 없음.

 

여러 의미를 담는 동일한 상수를 쓰지 않는것.

 

다음의 경우 실제 얻어지는 데이터를 사용하는것이 유용함.

  • 실제 실행을 통해 수집한 외부 이벤트의 결과를 이용해 실시간 시스템을 테스트하는 경우
  • 예전 시스템의 출력과 현재 시스템의 출력을 비교하려는 경우(병렬 테스팅)
  • 시뮬레이션을 리팩토링한 후 기존과 동일한 결과가 나오는지 확인하려는 경우

명백한 데이터

데이터의 의도를 어떻게 표현할 것인가?

-> 테스트 자체에 예상되는 값과 실제 값을 포함하고 이 둘사이의 관계를 드러내기위해 노력하라. 

 

다음에 코드를 읽을 사람도 생각해야 함.

 

명백한 데이터는 프로그래밍을 쉽게 한다는 장점.

 

이 오퍼레이션이 어디에 속할지 알아보기위해 가짜구현도 가능.

 

하지만 이미 정의된 기호상수가 있으면 그것을 사용하는것이 좋다.

 

 


26장. 빨간 막대 패턴

 

테스트를 언제 어디에 작성하고, 작성은 언제 멈출것인가?

 

한 단계 테스트

테스트를 고를 때 무엇을 기준으로 할 것인가?

-> 새로운 무언가를 가르쳐주고, 구현할 수 있다는 확신이 드는것.

 

'이건 할 수 있겠다' 라고 드는 구현의 확신이 드는 테스트.

 

전체중 간단한 사례를 나타내는 테스트에서 시작했다면 

이 테스트를 통해 자라는 프로그램은 하향식으로 보임.

 

전체의 작은 조각은 나타내는 테스트에서 시작해 조금씩 붙여나가는 식이면

이 프로그램은 상향식으로 작성된 것으로 보임.

 

- 이와 같은 수직적 메타포는 시간에 따라 변하는 단순화된 시각일 뿐,

'성장'은 자기유사성을 가진 피드백 고리를 암시. 환경이 프로그램에 영향을 주고 프로그램이 환경에 영향을 줌.

- 메타포가 어떤 방향성을 가질 필요가 있다면, 아는것에서 모르는 것의 방향성이 옳다.

 

 

시작 테스트

어떤 테스트부터 시작하는게 좋은가? -> 오퍼레이션이 아무일도 하지않는 경우부터 테스트.

 

  • 이 오퍼레이션을 어디에 두어야 하나?
  • 적절한 입력값은 무엇인가?
  • 이 입력들이 주어졌을때 적절한 출력은 무엇인가?

뭔가를 가르쳐줄 수있으면서 빠르게 구현할 수 있는 테스트를 선택하라.

 

 

설명 테스트

자동화된 테스트가 더 널리 쓰이게 하려면 어떻게 해야할까? 

-> 테스트를 통해 설명을 요청하고 테스트를 통해 설명해라.

 

혼자 TDD를 하면서 다른 팀원들도 TDD방식을 하기는 권하는것은 어려울것이다.

 

단순한 시작법은 테스트를 이용해 묻고, 테스트를 이용해 설명하는것.

 

학습 테스트

외부에서 만든 소프트웨어에 대한 테스트를 작성해야 할수도 있을까?

-> 패키지의 새로운 기능을 처음 사용하기전에 작성항 수 있다.

 

그냥 바로 사용하는 대신 API가 우리 예상대로 실행된다는것을 확인하는 작은 테스트를 만들어보자.

 

만약 테스트가 통과되지 않으면  어플리케이션도 실행되지 않을것이므로 실행해볼 필요도 없다.

 

일단 테스트가 통과한다면 애플리케이션은 항상 제대로 돌것이다.

 

 

또 다른 테스트

어떻게 하면 주제에서 벗어나지 않고 기술적인 논의를 계속할 수 있을까?

-> 주제와 무관한 아이디어가 떠오르면 이에대한 테스트를 할일 목록에 적어두고 다시 주제로 돌아올것.

 

하지만 엄격하게 한 주제로 묶는것은 좋은 아이디어를 억압함.

 

회귀 테스트

시스템 장애가 보고될 때 무슨일을 제일 먼저 하는가?

-> 그 장애로 인해 실패하는 테스트, 그리고 통화할 경우 장애가 수정되었다고 볼 수있는 테스트를 간단히 작성하라

 

회귀테스트 regression test는 원래 처음 코딩시 작성했어야 하는 테스트 이다.

 

이 시스템에서 사용자들이 무엇을 정확히 기대했고 무엇이 잘못되었는지 말할 기회를 준다.

 

시스템장애를 손쉽게 격리시킬 수 없다면 리팩토링 해야함.

이러한 종류의 장애는 곧 설계가 제대로 되지않았다는 것을 의미함.

 

휴식

지치고 고난에 빠졌을 땐 뭘해야 하나? -> 쉬는것이 좋다.

 

종종 거리두기를 통해 부족했던 아이디어가 나오기도 한다.

 

휴식의 역학

피로함 -> 판단력이 흐려짐 -> 계속 일하면 더 피곤

 

다시하기

길을 읽은 느낌이 들땐 어떻게 할까? -> 코드를 지워버리고 처음부터 다시해보자.

 

싸구려 책상, 좋은 의자

TDD를 할 때 어떤 물리적 환경이 적절한가? ->좋은 의자, 좋은 컴퓨터를 구해라.


27장. 테스팅 패턴

 

 

자식 테스트

지나치게 큰 테스트를 어떻게 돌아가도록 할 수 있을까?

-> 원래 테스트 케이스의 깨지는 부분에 해당하는 작은 테스트 케이스를 작성하고

    그 작은 테스트 케이스가 실행되도록 하라.

    그 후에 다시 원래의 큰 테스트 케이스를 추가하라.

 

빨강 / 초록 / 리팩토링 순의 단계를 유지하는 것이 중요.

 

테스트가 너무 크면 거슬리는 테스트를 삭제하고 작게 다시 시작.

 

모의 객체

비용이 많이 들고 복잡한 리소스에 의존하는 객체를 테스트하려면 어떻게 해야하나?

-> 상수를 반환하게끔 속임수 버전의 리소스를 만들면 된다.

 

모의객체 Mock Object 는 자료가 많다.

 

데이터베이스는 시작 시간이 오래걸리고 깨끗한 상태로 유지하기 어려움.

 

데이터베이스는 개발 중 많은 오류의 원인이 됨.

 

해법은 대부분의 경우 진짜 데이터 베이스를 사용하지 않는것.

대다수의 테스트를 진짜 DB처럼 행동하지만 실제는 메모리에 존재하는 객체를 통해 작성하자.

 

성능과 견고함에 더해 모의 객체는 가독성이 더 좋다.

 

모의 객체가 진짜 객체와 동일하게 동작하지 않으면 어떻게 될까?

- 모의 객체용 테스트 집합을 진짜 객체가 사용 가능해질때 그대로 적용해서 위험을 줄임.

 

셀프 션트 self shunt

한 객체가 다른 객체와 올바르게 대화하는지 테스트하려면 어떻게 할까?

-> 테스트 대상이 되는 객체가 원래의 대화 상대가 아니라 테스트 케이스와 대화하도록 만들면 됨.

 

- 테스팅 사용자 인터페이스의 초록 막대를 동적으로 업데이트 상황.

- UI 객체를 TestResult와 연결할 수 있다면 테스트가 실행된 시점, 테스트가 실패한 시점,

전체 테스트슈트가 시작되고 끝난 시점 등을 통보 받을수 있을 것.

- 이러한 이벤트를 통보 받으면 인터페이스를 갱신하면 됨.

 

셀트 션트 패턴을 이용해 작성하나 테스트가 그렇지 않은 테스트보다 읽기에 더 수월.

 

셀프 션트 패턴은 테스트 케이스가 구현할 인터페이스를 얻기 위해 인터페이스 추출 해야함.

셀프 션트를 위해 추출해 낸 인터페이스는 여러 곳에서 쓰이는 경우가 많음.

 

로그 문자열

메시지의 호출 순서가 올바른지 검사하려면 어떻게 해야 할까?

-> 로그 문자열을 가지고 메시지가 호출될 때마다 그 문자열에 추가하도록.

 

xUnit 예제에서 구현한 것처럼.

 

로그문자열은 옵저버 Observer를 구현하고 이벤트 통보가 원하는 순서대로 발생하는지 확인하고자 할때 유용.

 

순서에 상관없이 어떤 이벤트 통보가 일어나는것만 검사한다면,

문자열 집합을 저장하고 단언에서 집합 비교를 수행하자.

 

로그 문자열을 셀프션트와도 잘 작동함.

해당 테스트 케이스는 로그를 추가하고 적절한 값을 반환하는 식으로 셀프현트한 인터페이스의 메서드 구현.

 

크래시 테스트 더미

호출되지 않을것 같은 에러코드(발생하기 힘든 에러)를 어떻게 테스트 할것인가?

-> 실제 작업을 수행하는 대신 예외만 발생시키기만 하는 특수한 객체를 만들어 이를 호출.

 

테스트 되지 않은 코드는 작동하는 것이 아닌것으로 간주.

모두 테스트 할수 없으므로 작동하길 원하는 부분에 대해서만 하자.

 

테스트 케이스 안에 원하는 메서드 하나만 재정의 할수 있으므로

익명 내부 클래스는 테스트하기 원하는 적절한 메서드만이 오류를 발생시키게끔 하는데 유용함.

 

깨진 테스트

혼자 프로그래밍 할때 프로그래밍 세션을 어떤 상태로 끝마치는게 좋을까?

-> 마지막 테스트가 깨진 상태로 끝마치는것이 좋다.

 

프로그래밍 세션을 끝낼때 테스트 케이스를 작성하고 이것이 실패하는것을 확실히 확인

그 후 다시 시작하기 위해 돌아왔을때 깨진 테스트 부터 시작하면 된다.

 

깨끗한 체크인

팀 프로그래밍을 할때 프로그래밍 세션을 어떤 상태로 끝마치는게 좋을까?

-> 모든 테스트가 성공한 상태로 끝마치는것이 좋다.

 

체크인 하기전 항상 모든 테스트가 돌아가는 상태로 만들어 두어야 한다.

 

때로 통합 테스트 슈트에서 테스트가 실패하는 경우는 어떻게 할까?

-> 단순한 규칙은 코드를 없애고 다시 시작하는것. 

 

이 규칙을 따르고 팀원들이 자주 체크인을 한다면 작업을 날릴 위험이 적을것이다.


28장. 초록막대 패턴

 

 

가짜로 구현하기(진짜로 만들기 전까지만)

실패하는 테스트를 만든 후 첫번째 구현은 어떻게 하는게 좋을까?

-> 상수를 반환하게 하라. 일단 테스트가 통과하면 단계적으로 상수를 변수로 바꿈.

 

아직 모두 구현하지 않았지만 구현하면 안정적일것을 아는것.

 

가짜로 구현하기의 강력한 두가지 효과

  • 심리학적 효과 - 초록막대일때 자신의 위치를 알수 있고, 확신을 가지고 리팩토링 할수 있음.
  • 범위 scope 조절 - 구체적인 예에서 일반화하면 혼동되는 상황을 예방함. 물론 이전 테스트 작동이 보장되면 다음 테스트에 더 집중하게 됨.

필요없는 코드는 작성하지 말아야 하는가? 

-> 리팩토링 단계에서 코드 중복을 제거 하면됨.

 

삼각측량

추상화 과정을 테스트로 주도할 때 어떻게 보수적으로 할수 있나?

-> 오로지 예가 두개 이상일 때에만 추상화 하라.

 

삼각측량은 그 규칙이 명확하다

 

삼각측량 법칙은 단순해 보이나 무한 루프를 만들어내므로 

어떻게 올바르게 추상화 할것인지 감잡기 힘들때만 삼각측량을 사용하자.

그외엔 명백한 구현과 가짜로 구현하기에 의존.

 

명백한 구현

단순한 연산들을 어떻게 구현하는가? -> 그냥 구현하자.

 

확신이 든다면 명백한 구현을 입력.

그리고 빨간막대를 본다면 다시 단계를 밟아 진행.

 

제대로 구현하면서 깨끗한 코드를 만드는건 비현실적일수 있으므로.

빨강 / 초록 / 리팩토링의 리듬을 유지하는것이 바람직함.

 

하나에서 여럿으로

객체 컬렉션 collection 을 다루는 연산은 어떻게 구현하나?

->일단 컬렉션 없이 구현하고 그 다음에 컬렉션을 사용

 

점진적으로 대입하기.

변화 격리하기의 예

ex) 인자 단일값 하나 받아 구현 -> 인자에 컬렉션 추가, 테스트 케이스에도 인자 추가

-> 이후에 사용하지 않는 단일값 삭제후 대신 컬렉션 사용.

 

코드를 고쳐 테스트 케이스를 바꿔도 코드에 영향이 없도록 해서 

테스트 케이스를 개선할수 있었다.

'IT Book Summary > TDD' 카테고리의 다른 글

32장. TDD 마스터 하기  (0) 2020.04.18
29장 - 31장  (0) 2020.04.12
xUnit 21장 - 24장  (0) 2020.03.31
18장-20장 xUnit  (0) 2020.03.24
13-17장  (0) 2020.03.18