# TDD? BDD?

매일 수정하는 GNOSS LV5·2022년 4월 15일
1

AndroidStudio

목록 보기
69/83

TDD(Test-Driven-Development)

TDD의 정의

테스트 주도 개발이라고 부릅니다.
반복 테스트를 이용한 소프트 웨어 방법론으로써 작은 단위의(Unit Test)테스트 케이스를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복하며 구현합니다.

애자일 방법론 중 하나인 Test-First의 개념에 기반을 둡니다.


개발주기

위의 그림은 TDD의 개발주기를 표현한 것입니다.

<Red>단계에서는 실패하는 테스트 코드를 먼저 작성한다. 

<Green>단계에서는 테스트 코드를 성공시키기 위한 실제 코드를 작성한다.

<Yellow>단계에서는 중복 코드 제거, 일반화 등의 리팩토링을 수행한다.

중요한 것은 실패하는 테스트 코드를 작성할 때까지 실제 코드를 작성하지 않는 것과, 실패하는 테스트를 통과할 정도의 최소 실제 코드를 작성해야 하는 것이다. 이를 통해, 실제 코드에 대해 기대되는 바를 보다 명확하게 정의함으로써 불필요한 설계를 피할 수 있고, 정확한 요구 사항에 집중할 수 있다.


개발 프로세스

기존의 프로세스와 다릅니다.
기존의 프로세스는 디자인이 잡히면 코드개발을 진행하고 테스트를 진행합니다.
하지만 여기에서 디자인이 수정된다거나 클래스 단위의 개발을 진행하면서 개발자가 생소한 영역을 개발하게 된다면 설계를 처음부터 다시 진행해야 하는 일들이 발생합니다.

애자일 방법론을 따를 경우
해당 기획 , 디자인, 개발 방법에 대해서 불확실성이 높을 때 잦은 피드백과 협력이 중요하게 됩니다.
따라서 TDD와 애자일 방법론은 뗄수 없는 관계입니다.

💡 TDD는 작가가 책을 쓰는 과정과 유사합니다. 책을 쓸때 제일 먼저 목차를 구성하고 각 목차에 맞는 내용을 구상하여 초안을 작성하고 다시 읽으면서 고쳐쓰기를 반복합니다. TDD로 비유하자면 책(개발)을 쓸때 제일 먼저 목차(테스트 코드)를 작성하고 각 목차에 맞는 내용을 구상하여 초안작성(코드 개발)을 합니다. 다시 읽으면서 고쳐쓰기(리팩토링)를 진행합니다.

TDD의 장단점

TDD 개발 방식의 장점

  • 보다 튼튼한 객체 지향적인 코드 생산
    • TDD는 코드의 재사용 보장을 명시하므로 TDD를 통한 소프트웨어 개발 시 기능 별 철저한 모듈화가 이뤄진다. 이는 종속성과 의존성이 낮은 모듈로 조합된 소프트웨어 개발을 가능하게 하며 필요에 따라 모듈을 추가하거나 제거해도 소프트웨어 전체 구조에 영향을 미치지 않게 된다.
  • 재설계 시간의 단축
    • 테스트 코드를 먼저 작성하기 때문에 개발자가 지금 무엇을 해야하는지 분명히 정의하고 개발을 시작하게 된다. 또한 테스트 시나리오를 작성하면서 다양한 예외사항에 대해 생각해볼 수 있다. 이는 개발 진행 중 소프트웨어의 전반적인 설계가 변경되는 일을 방지할 수 있다.
  • 디버깅 시간의 단축
    • 이는 유닛 테스팅을 하는 이점이기도 하다. 예를 들면 사용자의 데이터가 잘못 나온다면 DB의 문제인지, 비즈니스 레이어의 문제인지 UI의 문제인지 실제 모든 레이러들을 전부 디버깅 해야하지만, TDD의 경우 자동화 된 유닛테스팅을 전재하므로 특정 버그를 손 쉽게 찾아낼 수 있다.
  • 테스트 문서의 대체 가능
    • 주로 SI 프로젝트 진행 과정에서 어떤 요소들이 테스트 되었는지 테스트 정의서를 만든다. 이것은 단순 통합 테스트 문서에 지나지 않는다. 하지만 TDD를 하게 될 경우 테스팅을 자동화 시킴과 동시에 보다 정확한 테스트 근거를 산출할 수 있다.
  • 추가 구현의 용이함
    • 개발이 완료된 소프트웨어에 어떤 기능을 추가할 때 가장 우려되는 점은 해당 기능이 기존 코드에 어떤 영향을 미칠지 알지 못한다는 것이다. 하지만 TDD의 경우 자동화된 유닛 테스팅을 전제하므로 테스트 기간을 획기적으로 단축시킬 수 있다.

TDD 개발 방식의 단점가장 큰 단점은 바로 생산성의 저하이다.

  • 개발 속도가 느려진다고 생각하는 사람이 많기 때문에 TDD에 대해 반신반의 한다. 왜냐하면 처음부터 2개의 코드를 짜야하고, 중간중간 테스트를 하면서 고쳐나가야 하기 때문이다. TDD 방식의 개발 시간은 일반적인 개발 방식에 비해 대략 10~30% 정도로 늘어난다.
    하지만 많이 본 문구겠지만 실제로 TDD 방식으로 개발을 진행하면 유지 보수, 디버깅에 많은 시간이 감소되어 궁극적으로는 최종 개발까지 더 짧은 시간이 소요된다고 합니다.

BDD(Behavior-Driven-Development)

BDD의 정의

행동 주도 개발이라는 의미로 사용자의 행위를 생각하며 테스트를 개발합니다.
BDD는 사실 TDD의 한 종류로 클라이언트 개발자들에게 좀 더 장점이 있는 개발 방법론입니다.
모듈, 메서드 위주의 TDD보다는 어떤 시나리오를 강조하기 때문입니다.

TDD를 하다보면 TestCase에 대한 유지보수, 일정의 압박, 예외케이스에 대해 매번 생각해야합니다.
하지만, 만약 기획이나 요구사항이 이미 작성되어 있다면 위와 같은 시간 비용이 줄어들게 될것입니다.

그렇다면 TDD,BDD 둘중 하나만 선택해서 개발자는 진행을 해야할까? 라는 의문이 생기게 됩니다.
하지만 이 둘은 분명 장단점이 극명하므로 상황에 맞추어 두가지를 골고루 사용해야할 것입니다.

프로젝트에서 BDD의 테스트 케이스로 시나리오에 대한 검증을 진행하고 시나리오에서 사용하는 각 모듈들은 TDD의 테스트케이스를 통하여 검증을 하는 방법을 진행해야 합니다.

BDD의 형식

  • Given : 사용자 행위시 주어진 환경 서술
  • When : 사용자 행위 서술
  • Then : 행위에 따른 기대 결과 서술
예시)
Given > 
    "로그인 상태에서"
When >
    "닉네임을 1글자만 작성"
		"닉네임에 금칙어가 포함"
		"닉네임에 특수문자 포함"

Then >
    "경고문구를 '두글자 이상으로 작성'이라고 보여줌"
    "경고문구를 '금칙어가 포함됨'이라고 보여줌"
    "경고문구를 '특수문자가 포함됨'이라고 보여줌"

Given , When, Then으로 나누어 지는 Flow를 사용합니다.

class MyTests4 : BehaviorSpec({
    given("100점이 만점인 상황에서") {
        val totalMarks = 100
        `when`("학생의 점수가") {
            and("90점 이상이라면") {
                val obtainedMarks = 99
                then("등급은 A") { getGrade(obtainedMarks, totalMarks) shouldBe "A" }
            }
            and("80점 이상 90점 미만이라면") {
                val obtainedMarks = 88
                then("등급은 B") { getGrade(obtainedMarks, totalMarks) shouldBe "B" }
            }
            and("70점 이상 80점 미만이라면") {
                val obtainedMarks = 77
                then("등급은 C") { getGrade(obtainedMarks, totalMarks) shouldBe "C" }
            }
            and("60점 이상 70점 미만이라면") {
                val obtainedMarks = 66
                then("등급은 D") { getGrade(obtainedMarks, totalMarks) shouldBe "D" }
            }
            and("60점 미만이라면") {
                val obtainedMarks = 34
                then("등급은 F") { getGrade(obtainedMarks, totalMarks) shouldBe "F" }
            }
        }
    }
})

fun getGrade(obtainedMarks: Int, totalMarks: Int): String {
    val percentage = getPercentage(obtainedMarks, totalMarks)
    return when {
        percentage >= 90 -> "A"
        percentage in 80..89 -> "B"
        percentage in 70..79 -> "C"
        percentage in 60..69 -> "D"
        else -> "F"
    }
}

private fun getPercentage(obtainedMarks: Int, totalMarks: Int): Int {
    return (obtainedMarks / totalMarks.toFloat() * 100).roundToInt()
}

BDD의 장점

  • 기획서상 요구사항을 BDD TC로 모두 작성한다면 서비스 설계에서의 모순점이 무엇인지 발견할 수 있다.
  • 불필요한 테스트를 작성할 필요가 없습니다.
  • 서비스에 대한 이해도가 높아집니다.

다만 BDD는 테스트 케이스를 만들어 줄 QA의 존재가 필요하며 기획자의 시나리오가 중요해집니다.
또한 BDD에 사용한 메서드에 대한 검증이 필요합니다.

profile
러닝커브를 따라서 등반중입니다.

0개의 댓글