TDD(Test-Driven Development)

여름방학부터 안드로이드를 학습할 겸, 동기들과 ‘I Time U’라는 포모도로(pomodoro) 앱을 만드는 프로젝트가 막바지에 다다랐다. 이 프로젝트를 통해 배운 점에 대해서는 머지 않아 포스팅하겠지만, 가장 크게 와닿은 것 중 하나는 바로 오늘의 주제인 TDD(Test-Driven Development)의 중요성이었다.

TDD를 사용하지 않은 이번 프로젝트에서의 한계는 명확했다. 테스트 코드를 작성하지 않으니 하나의 함수에 여러 기능이 들어가고, 그에 더해 미처 고려하지 못한 변인들로부터 버그가 발생했다.

물론 개발을 하며 버그가 생기는 건 당연한 현상이다. 문제는 그렇게 생긴 버그가 한 두 가지가 아니며, 그것을 찾고 픽스하는데에 걸린 시간이 적지 않았던데다, 결론적으로 기껏 힘들게 버그를 고치고 완성한 코드가 스파게티(!)의 형상이었다는 점이다.

spagetti

TDD의 필요성을 절감했으니 다음은 TDD에 대해 배우고 정리하는 단계이다. 학습을 위해 Microsoft Virtual Academy: Test-Driven Development 영상을 참고했다. 선행 과정은 딱히 없고, 프로그래밍의 기본 구조를 알고 있다면 이해하기가 그리 어렵지 않다. 언어가 영어로만 제공된다는 점에서 약간의 장벽은 있을 수 있지만, 자막을 보면서 천천히 따라가보면 이해할 수 있으리라 생각한다.

이하는 코스의 첫 번째 ~ 세 번째 모듈을 듣고 이해한 것을 정리한 글이다.

Test-Driven Development in a Nutshell

TDD는 테스트를 개발 프로세스에 통합시키는 것을 의미한다. 개발자는 코드가 하게 될 것에 대해 테스트 코드를 정의하고, 해당 테스트 코드를 통과하게 하기 위한 코드를 작성한다. 다만 주의할 점은 테스트를 통과하기 위한 것과 무관한 코드를 작성하지 않아야 한다는 것이다. 코드 작성에 대한 규칙은 다음과 같다.

프로그래밍 과정은 아래 그림과 같이 순환구조를 거친다.

red-green-refactor

이미지에 나타난 절차는 아래의 5가지 순서로 세분화 할 수 있다.

1) 테스트 코드를 추가한다.
2) 테스트를 실행하여 새로운 실패를 확인한다.
3) 코드를 약간 수정한다.
4) 모든 테스트를 통과해 전체 테스트의 성공을 확인한다.
5) 중복된 부분을 제거하여 리팩토링한다.

언뜻 보기엔 TDD가 비효율적으로 여겨질 수 있다. 테스트 코드를 작성하는 것도 귀찮고, 일부러 테스트 통과를 하지 말라고 하며, 코드 수정도 테스트를 통과할 정도로만 조금씩하니 답답할만도 하다.

그러나 역설적이게도, 단점으로 여겨지는 그 점들은 TDD의 이점으로 작용한다.

Types of automated tests

자동화 테스트의 종류와 그에 대해 필자가 이해한 정의는 다음과 같다.

Unit testing and Test-Driven Development

유닛 테스트는 개발을 하면서 가장 많이 작성하게 될 부분이다. 아래는 C#으로 작성된 유닛 테스트 코드의 양식이다.

1
2
3
4
5
6
7
8
9
[TestMethod()]
public void whenXHappens_GivenY_ResultShouldBeZ() {
  // arrange
  Thing newThing = new Thing();
  // act
  newThing.doSomething();
  // assert
  Assert.AreEqual("Actual", newThing.toString());
}

1: 테스트 메소드(method)임을 확인한다.
2: 메소드의 이름
3-4: 테스트를 위한 변수와 오브젝트를 구성한다.
5-6: 테스트를 동작한다.
7-8: 예상 결과가 발생했는지 확인한다.

유닛 테스트는 세 가지 조건을 만족해야 한다.

조건을 만족하는 유닛 테스트는 TDD의 장점을 가지며, 코드가 실제로 어떻게 동작하는지 알려주는 문서의 역할을 한다.