Specification
프로그램에서 터지는 버그는 대부분 동작에 대한 오해로 발생합니다.
이러한 동작의 오해를 줄이기 위한 대표적인 방법으로 Spec(명세)이 존재합니다.
또한 완성된 프로그램은 필수적으로 Spec을 갖고있어야 합니다.
그렇다면 Spec은 무엇을 의미하는 걸까요??
다음은 실제로 사용되는 BigInteger 클래스 Spec입니다.
Class BigInteger
public BigInteger add(BigInteger val)
Returns a BigInteger whose value is (this + val).
Parameters:
val - value to be added to this BigInteger.
Returns:
this + val
add 메서드의 Signature와 실행되는 동작에 대한 간단한 설명이 존재합니다.
이러한 명세를 통틀어 Spce이라 말합니다.
인터페이스에 존재하는 Abstract Method도 Signature를 명시해놓은 Spce의 일부로 볼 수 있습니다.
만약 Spec이 없다면 이 메서드가 어떤연산을 하는지 알 수 없으며 어떤 연산을 하는지 정해지지 않았기 떄문에
테스트코드 작성도 불가능해집니다.
또한 구현이 바뀔때마다 해당 메서드가 어떤 기능을 하는지 내부를 확인해야 알 수 있음으로 Spec 명시는 필수적입니다.
Contract
Contract(계약)은 클라이언트와 개발자의 책임을 의미합니다.
- 개발자의 책임 : 구현을 완료하고 Spec을 명시합니다. 또 Spec만 따르면 해당 기능은 정상적으로 동작함을 책임집니다.
- 클라이언트의 책임 : 개발자가 구현한 기능을 사용할때 Spec에 맞게 사용해야 한다.
이런 책임을 묶어 Contract라 지칭합니다. 또한 Contract의 개념을 적용시켜 PreCondition , PostCondition 이 만들어집니다.
PreCondition(전제조건) 은 클라이언트의 책임입니다.
만약 Spec이 다음처럼 명시돼있다면
Requirements : 중복이없는 배열
Effects : 중복없는 배열을 오름차순 정렬함
사용자는 Spec에 맞게 중복이없는 배열을 인자로 넘겨주어야 합니다.
그렇지않으면 Spec을 위반하는것으로 결과값을 올바르지 않을 수 있습니다. (이 경우에는 보통 Exception을 발생시켜 줍니다)
이러한 조건을 PreCondition 이라 합니다.
반면에 PostCondition(사후조건)은 구현자의 책임입니다.
전제조건이 맞으면 올바른 값을 반환해야 한다는 것이죠
이러한 조건을 이용해 테스트코드 작성이 가능합니다.
Test Code
우리는 Spec이 왜 필요한지 어떤 역할을 하는지 알아봤습니다.
이를 이용해 Test Code를 작성해 보겠습니다.
Test Code에는 크게 2가지 가 존재합니다.
- BlackBox Test
- 연산내부가 어떻게 구현됐는지 모름 , 오직 Spec에 대한 명시만으로 테스트 작성
- WhiteBox Test
- 연산내부 구현의 지식으로 테스트 작성
각각의 테스트는 장단점이 존재합니다.
BlackBox Test는 코드의 내부를 몰라도 된다는 장점이 있지만
테스트 케이스를 설계할 때 누락된 기능이 있을 수 있습니다.
또한 주로 입력과 출력에 중점을 두는 테스트 방법입니다.
WhiteBox Test는 코드의 내부 구조와 논리를 이해하고 검증하는 방법입니다.
따라서 논리적 결함을 발견하기 용이하지만 구조를 이해하고 있어야 하기 때문에 코드에 대한 지식이 필요합니다.
BlackBox Test
public static boolean inputPassword(String passWord) {
String regex = "^[0-9]*";
if (passWord.equals("")||!Pattern.matches(regex,passWord)) {
return false;
}
return true;
}
다음은 매우 간단한 연산이며 Spec은 다음과 같습니다.
Requirments : String Value
Effects : 비어있지 않은 숫자로만 이루어진 경우 True , 그외는 False 반환
우리는 이 코드의 내부를 전혀 모르지만 Spec을 통해 테스트 가능합니다.
System.out.println(inputPassword("1234")); // 숫자로만 이루어짐 , true
System.out.println(inputPassword("asd")); // 영어로만 이루어짐 , false
System.out.println(inputPassword("a123")); // 문자 영어 섞임 , false
System.out.println(inputPassword("")); // 빈 문자열 , false
(지금은 출력을통해서 메서드의 반환값이 올바른지 확인했지만 실제로는 JUnit5 혹은 Assert를 사용하면 됩니다.)
WhiteBox Test
public static int factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException();
}
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
다음은 팩토리얼 연산을 하는 메서드입니다.
이 메서드의 Spec은 다음과 같습니다.
Requirments : 계산할 팩토리얼의 대상이 되는 0이상의 정수
Effects : 정수의 팩토리얼 값
Spec에는 정수값을 넣었을때
이 코드의 내부의 구현에 대한 지식이 없다면 다음과 같은 테스트만 작성 가능합니다.
try {
factorial(-1);
} catch (Exception e) {
System.out.println("에러 체크");
}
매우 한정적인 테스트만 작성 가능함으로 WhiteBox Testing이 필요합니다.
assert factorial(5) == 120; // n이 양수인 경우
assert factorial(0) == 0; // n이 0인 경우
이 경우에 PreCondition 은 0이상의 정수를 의미하며
PostCondition은 올바른 팩토리얼 값을 의미합니다.
이 글에서는 Spec에 필요성 및 간단한 기초 활용법을 알아봤습니다.
이 글이 도움이 되셨으면 좋겠습니다.
'프로그래밍 기초 > 전산학 기초' 카테고리의 다른 글
| [불변타입 Vs 가변타입] (1) | 2023.10.28 |
|---|---|
| Collection FrameWork 자료구조의 이해 (3) | 2023.10.23 |
| [추상클래스 Vs 인터페이스] (0) | 2023.10.22 |
| 클래스 설계와 Abstraction Barrier (0) | 2023.10.22 |
| Abstraction(추상화) 기본 개념 - 1편 (0) | 2023.10.20 |