본문 바로가기
iOS/Swift

Swift) TDD

by Jiseong 2022. 2. 12.

TDD

TDDTest - Driven - Development 의 약자로 "테스트 주도 개발" 이라고 한다.

 

TDD와 Unit Test는 다르다.

 

Unit Test는 기능 개발을 진행한 후, 기능단위의 테스트 코드를 작성해 함수, 메소드의 기능을 개발자의 의도대로 테스트하는 것이다.

 

TDD와 달리 테스트 코드를 꼭 먼저 작성할 필요도 없고, 리펙토링도 포함하지 않는다. 순수하게 테스트 코드만 작성하는 것을 의미한다.

 

하지만 TDD의 경우 말 그대로 "테스트 주도 개발" 이다.
테스트를 주도적으로 하는 개발.. 말이 좀 어렵게 느껴질 수도 있다.

 

말그대로 받아들이면 된다.
기능 개발을 위한 코드를 먼저 작성했던 일반적인 방법과 달리, 테스트를 위한 코드를 먼저 작성한 후, 그 코드에 맞게, 테스트에 통과할 수 있게 기능 개발을 하는 것이다.

 

음.. 흔히 Testable한 코드로 기능 개발을 하는 것이다.

 

서로 연관되어있는 관계다

 

생각을 덧붙이자면.. TDD의 결과물이 Unit Test인 정도.. 

TDD Flow

사진 출처

TDD는 실패하는 테스트 케이스를 작성하고(Red), 작성된 테스트 케이스를 통과(Success)하는 코드(기능 개발)를 작성한 후(Green), 작성한 코드를 리팩토링하는(Blue) 세가지의 단계를 거친다.

 

1. 실패하는 테스트 케이스 작성 (Red)

테스트케이스는 실패하도록 작성한다.


근데 실패할 수 밖에 없다. 실제 기능 코드는 구현이 안되있기 때문에 컴파일 오류가 날 것이다.

 

실제 기능을 할 코드를 구현하여 위 사진 처럼 컴파일 오류가 사라질 정도만 구현을 해준다.

 

2. 작성된 테스트를 통과하는 코드 작성

테스트 코드를 성공시키기 위한 실제 코드(테스트 메소드가 아닌 실제 메소드)를 작성한다.

 

실패했던 테스트를 통과할 정도의 실제 코드를 작성해야한다.

 

여기서 완벽한 코드를 작성하려고 한다면, TDD의 취지에 맞지않게 불필요한 설게를 할 수 있으므로 정확한 요구사항을 하나하나 따지고 지키기 위해 통과할 정도만 작성한다.

 

3. 리팩토링

임시로 막 만든 변수 네이밍을 바꾸거나. 중복 제거를 하는 등의 리팩토링을 진행한다.

너무 간단한 코드라 리팩토링 할 것도 없지만..

 

리팩토링 후 다시 1번으로 돌아가 진행된다.

1 -> 2 -> 3 -> 1 ... 이런식으로 한 루트를 돌며 반복적으로 진행하는 것이다.

일반적인 개발방식과의 비교

일반적인 개발 방식

일반적인 개발 방식은 요구사항 분석 및 설계 -> 코드 개발 -> 테스트 -> 배포정도의 개발 주기를 갖는다.

 

어떤 개발 방식이든 문제점은 있겠지만, 일반적인 개발 방식의 문제도 단계적으로 알아보자.

  1. 요구사항이 완전히 픽스된게 아닐수 있고, 향후 변동가능성이 있음
  2. 초기 요구사항을 알았으니, 일단 요구사항대로 개발 -> 테스트 -> 배포
  3. 요구사항 변동 및 버그가 발생하여 코드 재설계
  4. 재설계, 버그 fix를 위해 코드가 삽입, 수정, 삭제되는 과정에서 불필요한 코드 발생, 중복 처리 발생
  5. 요구사항 변동 및 버그가 1번만 발생하는 것이 아니므로 위 과정 반복
  6. 코드가 번잡해져 재사용이 어렵고, 관리가 어려워져 유지보수 비용 증가
  7. 그래도 고쳐보려 버그가 발생한 부분을 수정했더니 이상한 곳에서 버그 또 발생
  8. "아 어떡하지.." 하며 다른 기능에서 문제가 발생할까봐 잘못된 기능을 하는 코드도 쉽사리 건들지 못하는 문제 발생
  9. 반복 -> 여기저기서 퍼퍼펑 -> 파멸 -> 다 갈아엎어야됨

사소한 수정에도 겁이나 모든 기능을 테스트해봐야되는 문제가 있고, 이로인해 테스트 비용이 증가한다.

TDD 개발 방식

TDD와 일반적인 개발 방식의 차이는 대표적으로 주기가 있다.

 

일반적인 개발 방식은 요구사항 분석 -> 개발 -> 테스트인 반면 TDD는 테스트 코드를 먼저 작성한 뒤에 실제코드를 작성한다. 

 

요구사항에 맞는 테스트 케이스를 작성하고. 테스트중 발생하는 버그, 수정사항들 또한 테스트 케이스에 추가하여 테스트 코드를 개선한다. 이후 테스트에 통과한 코드만을 개발 단계에서 실제 기능을 할 코드로 작성한다.

 

위 과정을 반복하며, 테스트 케이스에 가능한 모든 버그, 수정사항을 추가하며 실제 코드 실행시 버그가 줄어들고, 실제 코드가 간결해진다.

 

버그가 줄고, 코드가 간결해지니 자연스럽게 유지보수가 쉬워지고, 시간(유지보수시간)도 절감되는 등 비용이 줄어든다.

TDD 개발 방식의 장점

객체 지향적인 코드 개발

테스트 코드를 먼저 작성하고 그 테스트에 통과하기 위한 코드만 실제 코드로 작성하기때문에 각각의 함수를 정의할 때 각 기능들에 대해서 구조화시키게 된다.

 

테스트의 용이함을 위해 여러가지의 기능을 한 함수에 구현할 경우 테스트가 복잡해지고 시간이 오래걸리며 독립적이지 않아 재사용성도 현저히 떨어지게 된다.

 

그래서 위 같은 상황을 피하기위해 TDD의 목적인 코드의 재사용성을 보장하며 코드를 작성하게 된다. 

 

그러다보면 자연스럽게 재사용성을 기반으로 하게되고 그 코드는 객체지향적인 코드가 되는 것이다.

설계 수정 시간의 단축

사진 출처

실패할 테스트 코드 작성 후 성공할 테스트를 미리 작성하기 때문에 요구사항에 대한 많은 케이스들과 시나리오들을 그려보고 작성해봄으로써 코드 개발 전 기능 구현을 위한 예외 상황들을 미리 분석해보는 효과가 발생하여 예외 코드 작성이 수월해진다.

추가 구현의 용이함

개발이 이미 대부분 진행된, 혹은 완료된 코드에 요구사항이 추가되어 어떠한 기능을 추가할 때 가장 무서운 점은 추가할 기능이 기존의 코드에 뭔 짓을 할지, 어떤 영향을 미칠지 모른다는 것이다.

하지만 TDD의 경우 Unit Test를 이용하여 테스트 기간을 단축 시킬 수 있다.

디버깅 시간의 단축

위의 추가 구현의 용이함처럼 TDD는 기본적으로 단 Unit Test 기반의 테스트 코드를 작성하기 때문에 추후에 문제(버그, 요구사항 변경)이 생겼을 때 각 모듈(메소드)별로 테스트를 진행하여 문제가 발생한 곳을 쉽게 찾아낼 수 있다.

 

만일 TDD 방식의 개발이 아니라면 버그를 찾기위해 모든 코드를 확인하고 뒤져봐야되기 때문에 큰 프로젝트일수록 문제의 지점을 찾기가 굉장히 힘들어진다.

테스트 케이스의 문서화

발생할 수 있는 오류들을 전부 테스트 케이스에 적어놨기에 이런 테스트를 전부 통과했으니 이런 기능이 정상적으로 동작하겠구나하고 스펙 정의 문서로 대체될 수 있다.

코드의 자신감, 신뢰성 증가

요구사항을 분석하고, 많은 테스트 케이스들로 테스트하며 코드를 작성하였기 때문에 Testable한 코드로 이뤄져있고, 그말인 즉슨 각 기능들에 대한 함수 구조화가 확실히 되어있다는 것이다.

 

다시 말하면, 실제 코드에서 낮은 결합도와 높은 응집도를 가진다.

 

이로 인해 디버깅을 더 수월히 할 수 있고, 기능 분리가 되어있지 않았다면 발생했을 오류들이 발생하지 않기에, 코드 수정 또한 겁먹지 않고 할 수 있게된다.. (그래도 솔직히 겁은 날듯...)

TDD 개발 방식의 단점

생산성 저하

저~~기 위에서 말한 것처럼 일반적인 방식이 아닌 처음부터 테스트 코드도 짜고, 테스트 케이스에 맞게 실제 코드도 작성하고, 수시로 실패해보고, 실패한 코드 수정하고, 리팩토링하고.. 중간중간 테스트를 하면서 고쳐나가야 하기 때문이다.

 

개발시간은 실제로 일반적인 개발 방식에 비해 약 20~30%정도 늘어난다고들 한다.

 

가장 치명적인 단점이다. 대부분의 TDD를 실천지 않는 이유가 이때문이 아닐까?

한줄 설명

빠르게 실패하고, 피드백을 받아 개선해나가는 것

'iOS > Swift' 카테고리의 다른 글

Swift) map  (0) 2022.02.12
Swift) DoubleStack - Queue  (0) 2022.02.12
Swift) UIViewController Life Cycle  (0) 2022.02.12
Swift) MVC  (0) 2022.02.12
왜 디자인 패턴을 중요시 여기는가?  (0) 2022.02.12

댓글