책 리뷰

[책] 테스트 주도 개발 - TDD

seungdols 2016. 10. 20. 19:51
#테스트주도개발-TDD#

#TDD입문자#입문자가 볼만하다. 개정판이나 현재판으로 봤으면.. 하지만 출판된지 오래 되어 아쉬운 점이 있었다.



  • 저자 : 채수원
  • 출판사 : 한빛미디어


배운점


TDD

assertEquals(예상값, 실제값);
assertEquals("설명", 예상값, 실제값);

TDD에서 중요한 점

  1. 목표 이미지를 미리 세운다.
  2. 자동화된 테스트 케이스를 작성한다.
  3. 만족하는 로직을 작성하고 정제한다.

보통 테스트에 사용할 자원이나 객체들을 준비해놓는 부분을 픽스처라고 부른다.
대부분의 테스트 프레임워크는 픽스처를 지원한다.

TDD 장점

  • 개발의 방향을 잃지 않게 유지해준다.
  • 품질 높은 소프트웨어 모듈로 만들어 준다.
  • 자동화된 단위 테스트 케이스를 갖게 된다.
  • 사용설명서 & 의사소통의 수단이 된다.
  • 설계 개선하는데 도움이 된다.

로버트 마틴의 테스트 코드 작성법

  1. 실패하는 테스트를 작성하기 전에는 절대로 제품 코드를 작성하지 않는다.
  2. 실패하는 테스트 코드를 한 번에 하나 이상 작성하지 않는다.
  3. 현재 실패하고 있는 테스트를 통과하기에 충분한 정도를 넘어서는 제품 코드를 작성하지 않는다.

JUnit이 기본적으로 제공하는 기능은 아래와 같다.

  • 테스트 결과가 예상과 같은지 판별해주는 단정문
  • 여러 테스트에서 공용으로 사용할 수 있는 테스트 픽스처
  • 테스트 작업을 수행할 수 있게 해주는 테스트 러너
assertSame([message], expected, actual)//두 객체가 정말 동일한 객체인지 주소값으로 비교하는 단정문
assertNotSame([message], expected, actual)

메세지 박스가 있는 경우는 캐시 기능을 만들었는데, 해당 캐시가 정확히 동작하는지 판단할 필요가 있는 경우에 사용한다.

cache.add(sameObject, KEY);
assertSame("캐시처리 실패", sameObject, cache.lookup(KEY));

비슷하게 싱글톤으로 만들어진 객체를 비교할 때에도 사용한다.

static public void assertSame(String message, Object expected, Object actual) {}
  if (expected == actual) {
    return;
  }
  failNotSame(message, expected, actual);
}

위 코드는 실제 assertSame 메소드의 구현 코드이다.

테스트 코드를 작성 중 완전히 작성하지 못한 경우 fail()를 추가해두면 좋다.

Junit 4 특징

  • Java5 어노테이션 지원
  • 메소드 명이 test로 시작하지 않아도 된다.
  • 좀 더 유연한 픽스처 제공
    • @BeforeClass
    • @AfterClass
    • @Before
    • @After
  • 예외 테스트
    • @Test(expected=NumberformatException.class)
  • 시간 제한 테스트
    • @Test(timeout=1000)
  • 테스트 무시
    • @Ignore(“This method isn’t working yet”)
  • 배열 지원
    • assertArrayEquals([message], expected, actual)
  • @RunWith(클래스이름.class)
    • JUnit Test 클래스를 실행하기 위한 러너를 명시적으로 지정한다.
    • @RunWith는 junit.runner.Runner를 구현한 외부 클래스를 인자로 갖는다.
  • @SuiteClasses(Class[])
    • 보통 여러 개의 테스트 클래스를 수행하기 위해 쓰인다. @RunWith를 이용해 Suite.class를 러너로 사용한다.
  • 파라미터를 이용한 테스트
@RunWith(Parameterized.class)
@Parameterspublic static Collection data() {

}

@SuiteClasses

Suite 클래스는 JUnit3에서의 static Test Suite() 메소드와 동일한 일을 수행한다.
하지만, JUnit4에서는 어노테이션을 통해 일괄적으로 테스트를 수행 할 수 있다.

@RunWith(Suite.Class)
@SuiteClasses(Atest.class, BTest.class, CTest.class)
public class ABCSuite {

}

Hamcrest 라이브러리

assertEquals 대신 assertThat이라는 구문을 사용하기를 권장한다.

assertThat(테스트 대상, matcher 구문);
assertThat("메시지", 테스트 대상, matcher 구문);

Junit 라이브러리 구조 자체에 대한 설명을 볼 수 있는 sourceforge-junit에서 패턴, 원리에 대해 설명한다.

Mock

Mock 객체란, 실제 객체를 만들어 테스트 하기에는 힘들어 가짜 객체를 만들어 사용하는 데 쓰이는 객체를 Mock 객체라고 한다.

언제 사용하는가 ?

  1. 환경 구축을 위한 작업 시간이 많이 필요한 경우에 Mcok 객체를 사용한다.
  2. 경우에 따라서는 특정 모듈을 아직 갖고 있지 않아서 테스트 환경을 구축하지 못할 수도 있을 때 사용한다.
  3. 타 부서와의 협의나 정책이 필요한 경우에도 Mock 객체가 필요하다.
  4. 테스트가 특정 경우나 순간적인 상황에 의존적일 경우
  5. 테스트 시간이 오래 걸릴 경우

테스트 더블

  • 오리지널 객체를 사용해서 테스트를 진행하기가 어려울 경우 이를 대신해서 테스트를 진행할 수 있도록 만들어주는 객체를 지칭한다.

그러나 Mock이라는 단어를 더 많이 사용한다.

더미 객체

  • 인스턴스화 될 수 있는 수준으로만 만든 가짜 껍데기를 말한다.
  • 기능이 없으므로 더미 객체의 메소드 호출시 예외 발생을 추가하는 것도 방법이다.

테스트 스텁

  • 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어 놓은 객체
  • 객체의 특정 상태를 가정해서 만들어 놓은 단순 구현체이다.

페이크 객체

  • 복잡한 로직이나, 객체 내부에서 필요로 하는 다른 외부 객체들의 동작을 비교적 단순화 하여 구현한 객체이다.
  • 결과적으로는 테스트 케이스 작성을 진행하기 위해 필요한 다른 객체들과의 의존성을 제거하기 위해 사용된다.

테스트 스파이

  • 테스트에 사용 되는 객체에 대해서도, 특정 객체가 사용 됐는지, 그 객체의 예상 된 메소드가 정상적으로 호출 됐는지를 확인해야 하는 상황이 발생한다. 보통은 호출 여부를 몰래 감시해서 기록했다가

상태 기반 테스트 vs 행위 기반 테스트

상태 기반 테스트

  • 특정한 메소드를 거친 후, 객체의 상태에 대해 예상 값과 비교하는 방식

행위 기반 테스트

  • 리턴 값이 없거나 리턴 값이 있더라도 해당 값으로는 예상대로 동작 했는지에 대한 확인이 어려운 경우 활용

개발 영역에 따른 TDD 작성 패턴

생성자 테스트

  • 단순히 클래스를 생성하는 생성자는 굳이 테스트 클래스를 작성 할 필요가 없다.
  • 단, 객체서의 생성자의 의미뿐만 아니라 선행조건이나 업무로직을 직접 기술하는 경우도 있다.

DTO 스타일의 객체 테스트
특정한 목적을 갖고 만들어진 불변 객체의 경우에는 getter 메소드나 상태의 값을 확인할 수 있다.

TDD 유의사항

  • 테스트 케이스는 이름이 중요하다.

    testWithdraw_통장_잔고한계이상으로_인출요구시(){}
    

    위처럼 한글 메소드명도 가능하므로 이해하기 쉬운 이름을 작명해야 한다.

  • 제대로 동작하지 않는 테스트 케이스는 제거한다.

  • TDD는 자동화 된 테스트를 만드는 것이 최종 목표가 아니다.
  • 모든 상황에 대한 테스트 케이스를 만들 필요는 없다.
  • 여러 개의 실패하는 테스트 케이스를 한 번에 만들지 않는다.
  • 하나의 테스트 케이스는 하나만 테스트하도록 작성한다.
  • 전통적인 테스트 기법을 배워두자.
  • 테스트 케이스는 최대한 고립시킨다.

주의 사항

  • 테스트 케이스는 무엇을 테스트하는 것인지 알 수 있어야 한다.
  • Object Null check를 해야 한다.
  • 캡슐화를 지켜야 한다.
  • 한 번에 만들어지는 문자열을 + 대신 StringBuilderStringBuffer로 만들면 더 느릴 수 있다.
  • 테스트 항목과 테스트 내용을 맞추어야 한다.
  • 불변객체를 의도했다면, 불변객체를 표시해주도록 한다.
  • 코드에 순서가 필요하면 그것도 테스트한다.
상세 후기


생각해보면, 이 책은 내 책상 옆자리에 있어서 읽게 되었고, 인턴기간 때 받았는데 왜 이걸 이제서야 읽었나라는 생각도 들었다. TDD가 정말 애자일이나 무조건 테스트 코드부터 작성이라는 식의 오류를 범하지 않을 수 있게 되서 다행이다. 
어쨌거나 중요한건 테스트 코드가 잘 작성 되어야 한다는 것이고, 그로인해 주석이 줄어듬과 함께 프로젝트의 흐름을 파악하는 코드 문서가 될 것이라는 점이다. 

이 책이 좋은점은 쉽게 쓰여졌다는 점이고, 중간 비약하는 점은 좀 의아하긴 하지만 그래도 입문자들에게 (나같은..) 좋은 책인것은 확실하다.

그리고 또 한가지는 다양한 것들을 추천한다는 점이 좋다. 뭐랄까 테스트에 대한 도구(라이브러리등)이 다양하게 소개가 된다. 물론 그것들을 다 쓸리는 없다. 그럼에도 불구하고 이 책을 읽기를 권한다. 그리고 최신의 내용이 담긴 책 또한 읽어야 하지 않을까 싶다. 

테스트 코드를 잘 작성하고, 명확하게 작성하는 습관이 있다면 나중에라도 도움이 되지 않을까 싶다. 그리고 코딩의 질도 쌓일 수록 증가 될터이니 누이 좋고 본인 좋고 하지 않을까.



남의 책을 읽는 데 시간을 보내라. 남이 고생한 것에 의해 쉽게 자기를 개선할 수 있다 - 소크라테스 - 


반응형