- Published on
Software Testing 2 | 명세 기반 테스트를 통한 테스팅 프로세스 정리
- Authors
- Name
- 이건창
Introduction
오늘은 명세 기반 테스트와 관련된 이야기를 가져와봤어. 테스트 할 떄마다 코너 케이스 누락이 없는지 항상 고민을 많이 했는데 명세 기반 테스트 덕분에 어느정도 해소할 수 있었어.
명세 기반 테스트
명세 기반 테스트는 의미대로 요구사항 대로 테스트를 수행하는 방법을 말해. 그리고 요구사항에서 얻을 수 있는 정보를 활용해 (1)체계적
으로 (2)광범위
하게 테스팅 할 수 있도록 도와줄거야.
요구사항 정보로는 부족해
만약 문자열의 길이를 반환하는 메서드를 만들어야 한다고 상상해보자. 그럼 우리는 요구사항을 다음처럼 도출하겠지.
- 문자열을 입력받는다.
- 문자열의 길이를 출력한다.
우린 명세를 통해서 알 수 있는건 문자열을 입력하면 숫자가 반환됨을 알 수 있어. 그럼 우린 명세에 맞게 ab
가 입력되면 2
, abc
가 입력되면 3
을 반환하는 테스트를 고민 하겠지.

그럼 문자열 10개
나 20개
그리고 3000개
는 어떻게 판단 할 수 있을까? 위 테스트만 수행 한다면 나머지는 다 통과 된다고 확신 할 수 있어? 혹시 내가 놓친 테스트 케이스가 없지 않을까?
테스트는 가능한 많으면 좋음을 우리는 알 수 있지만, 전부를 테스트 하는건 불가능해. 우리는 실용적이게 테스트 범위와 횟수를 고려할 필요가 있어. 이런 고민은 명세 기반 테스트
와 함께하면 효율적인 프로세스를 구축 할 수 있어.
명시 기반 테스트 도출 방법
책의 저자는 다음과 같은 단계를 통해 적은 횟수의 테스트로 넓은 범위의 코드를 검증 할 수 있다고 해.
- 1 단계 : 요구 사항과 입출력 이해
- 2 단계 : 프로그램 동작 이해
- 3 단계 : 입출력 구획 탐색
- 4 단계 : 경계 분석
- 5 단계 : 테스트 케이스 고안
- 6 단계 : 테스트 케이스 자동화
- 7 단계 : 테스트 스위트 강화
책의 모든 내용을 담기도 어려울 뿐더라 이때까지 테스트를 작성해 본 개인적인 경험으로 다음처럼 줄일 수 있어 보여.
- 1 단계 : 요구사항 및 프로그램 이해
- 2 단계 : 입출력 구획 탐색 및 경계 분석
- 3 단계 : 테스트 케이스 구현
그럼 3 단계로 이야기를 이어나가볼게.
1 단계 : 요구사항 및 프로그램 이해
해당 과정에서 신경쓸 점은 테스트 할 행위는 무엇을 수행하는가
야. 도메인에 대한 이해가 있어야 입력에 대한 출력을 추론 할 수 있어.
최근 로또 생성기를 구현하면서 콘솔에 금액을 입력받는 행위를 구현한 적이 있어. 우리는 쉽게 문자열을 입력받아 숫자를 반환해야 함
을 도출 할 수 있지.
예상할 수 있는 코드는 다음과 같아.
import java.math.BigDecimal
fun requestAmount(str:String): BigDecimal {
return str.toBigDecimal()
}
2 단계 : 입출력 구획 탐색 및 경계 분석
우리가 처음 이야기 했던 것처럼 테스트 케이스 전부를 테스트 하는건 불가능해. 하지만 프로그램은 일부 입력 집합
에 대해 동일한 방식으로 동작하는 특성을 고려해 테스트 횟수를 줄일 수 있어.
다시 콘솔에 금액을 입력받는 행위로 돌아간다면 문자열에 숫자 외의 문자가 포함되지 않는다면 숫자다.
임을 알 수 있지. 우리가 고려할 수 있는 테스트는 다음과 같아.
- 문자열에 숫자 만 포함되는 경우 (ex.
"3000"
,"2000"
,"1000"
) - 문자열에 숫자와 문자가 함께 포함되는 경우 (ex.
"d32k3"
,"kk3jd"
,"k3j4"
) - 문자열에 문자 만 포함되는 경우 (ex.
"hello"
,"invalid"
) - 문자열이 비어있는 경우 (ex.
""
) - 문자열이
null
인 경우 (ex.null
)
예시에 포함되는 경우 동일한 방식으로 동작하기 때문에 모든 조합을 고려할 필요가 없어지지.
그런 다음 경계를 분석할 필요가 있어. 버그는 경계에서 자주 발생하게 돼. 경계는 입력받고 싶은 값과 그렇지 않은 값 사이를 의미하게 돼.
입력받는 값이 숫자인 경우 숫자인 문자열과 그렇지 않은 문자열 사이가 경계가 될 수 있어. 또한 숫자 중에서도 소수점이 있거나 0 이거나 음수인 경우도 경계를 구분 할 수 있지.
경계를 발견하면 접점과 거점을 찾는다면 빠르게 테스트를 찾을 수 있어. 접점은 입력받고 싶은 값
, 거점은 입력받고 싶지 않은 값
으로 구분 할 수 있어.
3 단계 : 테스트 케이스 구현
테스트 할 입력값을 고민했다면 테스트 케이스를 구현하면 돼. 이 때 JUnit
, Kotest
를 활용한다면 테스트 자동화가 가능해.
class AmountTest : FunSpec({
test("금액은 입력받으면 BigDecimal 자료형을 반환한다. 입력받은 금액 : 3000") {
requestAmount("3000").getBigDecimal() shouldBe BigDecimal(3000)
}
}
만약 여러 값을 테스트하고 싶다면 스켈레톤 코드를 늘리지 않고 다음처럼 구성할 수 있어.
listOf(
"2400",
"3000",
"4000",
).forEach {
test("금액은 입력받으면 BigDecimal 자료형을 반환한다. 입력받은 금액 : $it") {
requestAmount(it).getBigDecimal() shouldBe BigDecimal(it)
}
}
현재는 접점과 외점 테스트를 두 개 만들어서 테스트 하고 있어.
- 금액은 입력받으면 BigDecimal 자료형을 반환한다.
- 잘못된 문자열을 입력받으면 예외가 발생한다.
저자가 말하는 명세 기반 테스트
저자는 7 단계
로 나눠서 작성한 테스트 스위트에 확신이 설 때까지 반복하고 있나봐.

필자는 종종 테스트 케이스를 작성할 때 구획이나 경계를 놓치게 되면 이전 과정으로 돌아가서 테스트 스위트를 개선한다.
필자는 종종 테스트 케이스를 놓치면 스트레스 받았는데, 괜히 스스로를 비난하는 행동을 하진 말아야 겠어. 이런 생각으로 인해 점점 소극적으로 행동하게 되더라구. 효율적으로 테스트를 수행하게 된다면 필수불가결하게 발생하는 행동이니 마음에 담지 말자.
마지막으로
책을 읽으면서 가장 도움이 됐던건 경계를 판단하는 방법이었어. 접점과 거점 또는 내점과 외점으로 구분해서 어떤 조합을 테스트 할 지를 명확하게 판단 할 수 있었어.
원래는 요구사항을 재분석해 입력값을 판단해오는 행동으로 인해 코너 케이스가 잘 검출되지 않았어. 이 책 덕분에 입력값을 신중히 분석 할 수 있는 방법을 얻게 됐어.
최근 코틀린 페어 프로그래밍 스터디를 하고 있는데, 참여하면서 명세 기반 테스트를 최대한 활용해보려 해 ㅎㅎ 주변 사람들에게도 적극 권장해야겠다.