오늘은 화이트박스 테스트라고도 불리는 구조 기반 테스트와 코드 커버리지에 대해서 정리해봅니다.
Testing based on an analysis of the internal structure of the component or system.
Synonyms
clear-box testing, code-based testing, glass-box testing, logic-coverage testing, logic-driven testing, structural testing, structure-based testing
우리가 화이트박스 테스트라고도 부르는, 구조 기반 테스트는 소스 코드의 구조를 사용하여 테스트를 설계 / 수행하는 테스트입니다. 주로 단위테스트 / 대부분의 통합테스트가 여기에 해당하고, 직접 소스코드를 개발한 개발자들이 수행하기도 합니다.
이 소스코드의 구조에 기반해서 테스트를 짠다는 것은, 결국에는 어떤 코드가 테스트가 되었는지를 나타내는, 어떠한 커버리지를 도출해낼 수 있다는 의미이기도 합니다.
대부분의 개발자들은 적어도 명세에 기반하여, 최소한의 기능에 대한 단위 테스트 코드는 작성할 것입니다. 이 때 커버리지를 측정하는 어떠한 툴, 프레임워크 (Jacoco라던지.. 인텔리제이에서 단위테스트 코드를 실행할 때 커버리지를 같이 측정할 수도 있음.) 등을 이용하여 어떤 코드가 테스트되었는지를 확인하고, 테스트되지 않은, 실행되지 않은 코드에 대해서
를 판단한 후에 단위테스트 코드를 보강해나갑니다.
커버리지를 도출해내는 기준으로는 몇가지가 있습니다.
구문 커버리지는 코드라인이 적어도 한 번은 수행되면 계산하는 커버리지입니다.
분기 커버리지는 if, for, while, switch 등에서 분기가 갈리는 코드에 대한 수행여부를 의미합니다.
if x > 0 && (y > 0 || z <= 0):
~~~
else:
~~~
라는 코드가 있을 때, 파라미터 x, y, z
의 값이 어떻든 간에 조건식이 true가 되는 경우 한 번, 그리고 조건식의 결과가 false가 되어 else를 통하는 결과 한 번, 즉 2번의 케이스가 있을 것 입니다.
조건 커버리지는 분기 커버리지에서 더 나아가, 분기 내의 파라미터의 각각의 조건들에 대한 커버리지를 의미합니다.
if x > 0 && (y > 0 || z <= 0):
~~~
else:
~~~
일 때, 각각의 조건에 대해서는 아래와 같을 것 입니다.
True | false | |
---|---|---|
x | 1 | 0 |
y | 1 | 0 |
z | 0 | 1 |
조건 커버리지는 각각의 파라미터가 참과 거짓을 충족해야합니다.
프로그램이 수행할 수 있는 모든 경로를 수행해야합니다. 사실 이것이 가장 좋겠지만, 작성해놓고 보면 비즈니스 로직 상 도달 불가능한 코드가 있다던지, 재현이 매우 어렵다던지 하는 조건들이 있을 수 있습니다. 대부분의 경우에 경로 커버리지는 고려하지 않습니다.
분기뿐만 아니라, 해당 분기내의 각각의 조건에 대해서도 고려합니다.
if x > 0 && (y > 0 || z <= 0):
~~~
else:
~~~
위의 경우에 전체 분기를 통과하는 파라미터 조건들을 망라합니다.
No | x | y | z | result |
---|---|---|---|---|
1 | T | T | T | True |
2 | T | T | F | True |
3 | T | F | T | True |
4 | T | F | F | False |
5 | F | T | T | False |
6 | F | T | F | False |
7 | F | F | T | False |
8 | F | F | F | False |
이 경우의 커버리지는 아래와 같습니다.
위 예시는 파라미터가 3개밖에 안되기 때문에 테스트케이스 수가 적어보이지만, 분기가 늘어난다던지, 조건이 늘어나면 테스트해야할 케이슫르은 매우 많이 늘어날 것 입니다. 때문에 테스트 해야할 조건을 줄여야합니다.
MC/DC는 모든 분기와 조건의 조합을 고려하나, 모든 조합을 테스트하는 대신에, 테스트가 필요한 중요한 조합을 찾아내는데에 중점을 둡니다. 특정 조건을 수행할 때, 다른 조건과는 상관없이 전체 결과에 영향을 미치는 조건만을 테스트합니다. 또한 각각의 파라미터는 적어도 한 번은 최종 결과에 영향을 주어야합니다.
조건이 boolean과 같은 이진 결과라면, MC/DC 커버리지를 달성하는데에 필요한 테스트 케이스 갯수는 파라미터 N
개에 대해서, 케이스 N+1
개 입니다.
다시 한 번, 위에서 작성한 전체 분기를 통과하는 표를 확인하겠습니다.
No | x | y | z | result |
---|---|---|---|---|
1 | T | T | T | True |
2 | T | T | F | True |
3 | T | F | T | True |
4 | T | F | F | False |
5 | F | T | T | False |
6 | F | T | F | False |
7 | F | F | T | False |
8 | F | F | F | False |
위 조합에서,
을 찾아냅니다.
x
파라미터의 경우, No.1과 No.5이 각각 y
, z
의 값과는 상관없이 최종 결과값에 영향을 줍니다. 또한 No.2와 No.6도 각각 y
, z
의 값과는 관계없이 최종 결과값에 영향을 줍니다. 마찬가지로 No.3, No.7을 유추해볼 수 있습니다.
No.1, No.5 / No.2, No.6 / No.3, No.7
y
파라미터의 경우 No.1과 No.3이 있는데, 이것은 결과값에 영향을 미치지 않습니다.No.2와 No.4의 경우 결과값이 바뀌므로 해당할 수 있습니다.
No.2, No.4
z
의 경우 No.1과 No.2가 해당되나 결과값 자체는 바뀌지 않습니다. No.3과 No.4의 경우 결과값이 바뀌므로 해당된다고 할 수 있습니다. No.7과 No.8은 결과값이 바뀌지 않습니다.
No.3, No.4
최종 테스트 케이스 수는, 연구를 통해 N+1
개 만 있으면 된다는 사실을 알고 있습니다. y
, z
에 대해서는 각각 한 쌍의 패턴밖에 없으므로 꼭 필요합니다.
이미 유추한 케이스에 대해서는 No.2, No.3, No.4가 있는데, True 패턴은 2개이고 False 패턴이 1개밖에 없으므로, 적당히 No.6이나 No.7을 선택해주면 될 것 같습니다.
고로 최종적으로는 아래 4개의 케이스만 수행하면 최소한의 케이스로 최대한의 커버리지를 달성한다고 말할 수 있을 것 입니다.
구조적 테스트의 종류와 커버리지 등에 대해서 알아보았습니다. 각각의 수행방법은 방법이 조금씩 다르면서도, 테스트 구현의 어려움이 있는 경우도 있습니다. 어떤 방법을 수행했을 때는 부족한 부분이 꼭 발생하게 됩니다.
분기 커버리지가 100%라고 한다면, 구문 커버리지는 100% 충족한다고 말할 수 있습니다. 그러나 반대로 구문 커버리지가 100%라고 해서 분기 커버리지가 100%냐면 그것은 아닙니다.
if(a){
if(b){
bool statement1 = true;
}
}
위와 같은 코드가 있다고 할 때, 구문 커버리지 100%를 채우기 위해서는 a
와 b
에 대해 각각 true값이 들어가면 됩니다.
그러나 이 경우에 false의 경우를 테스트하지않으므로 분기 커버리지가 100%가 될 수 없습니다.
대체적으로 각각의 커버리지는 아래와 같습니다. (좌측이 최상위)
경로 커버리지 > MC/DC > 조건과 분기 커버리지 > 분기 커버리지 > 구문 커버리지
사실 분기테스트, 조건 테스트는 명세 기반의 Blackbox 테스트에서도, 특정 조건에 대해 사용할 수 있습니다.
기획서를 매우 세세히 작성하는 PO/PD의 경우에는 UI의 목업 혹은 화면 기획서에 각각의 input 필드에 대해 조건값과 기댓값을 매우 상세히 기재하는 경우가 있습니다. 테스트하는 입장에서는 매우 감사한 사람들인데, 이 경우 입력값과 기댓값을 확실히 알고 있으므로 결정표 (Decision table) 을 작성해서 테스트케이스를 작성한다면, 위에서 언급한 분기/조건에 대해 테스트한다고 말할 수 있을 것입니다. (실제 실행되는 코드는 알 수 없으므로 코드 커버리지까지는 확인할 수 없을 것 입니다.)
혹자는 개발자들이 단위테스트에서 이러한 테스트를 수행할 것인데, 굳이 블랙박스 테스트를 수행할때도 해야하는지 의문을 제기할 수도 있습니다. 그러나 저는 아래와 같은 이유로, 충분히 가치가 있다고 생각합니다.
ref