반응형

제어 흐름 테스팅

  • 테스트를 위한 모델로 소스 프로그램의 제어 흐름을 사용하는 구조적 테스팅 전략의 하나이며, 단위 테스팅에 적용하기에 적합함
  • 프로그램의 제어 구조를 충분히 커버하도록 경로(paths)를 선택함으로써 테스트 케이스가 개발되며, 선택된 경로들은 테스팅 완전성(testing thoroughness)’을 가늠하는 척도가 됨

 

제어 흐름 그래프(Control Flow Graph: CFG)

  • 프로그램의 제어 구조를 그래픽으로 표현한 것
  • 제어 흐름 그래프(G)가 노드들의 집합(N)과 에지들의 집합(E)으로 구성됨. , G = (N, E)
  • 아래와 같은 세 가지 종류의 노드가 존재함
    -
    분기점(decision) 또는 브랜치(branch) 노드: 제어가 두 개 또는 그 이상으로 갈라질 수 있는 프로그램 지점(, if , case )
    -
    합류점(junction) 또는 조인(join) 노드: 제어 흐름이 합쳐질 수 있는 프로그램 지점(, end if, end loop)
    -
    프로세스 블록(process block): 분기점이나 합류점에 의해 방해 받지 않는 연속된 프로그램 문장들로, 하나의 입구(entry)와 하나의 출구(exit)를 가진다.
  • 노드 A와 노드 B 사이의 단일 방향 에지는 A로부터 B로의 가능한 제어 흐름을 나타냄

 

[짝수 합계를 하는 프로그램의 제어 흐름 그래프 예1]

 

[짝수 합계를 하는 프로그램의 제어 흐름 그래프 예2]

 

제어 흐름 그래프 기반 테스트 케이스 도출

  • 제어 흐름 그래프의 입구(entry) 노드에서부터 출구(exit) 노드까지의 완전한 경로가 하나의 테스트 케이스가 됨. , 해당 경로가 실행되도록 테스트 입력 데이터를 준비
  • 작은 프로그램 루틴에도 입구에서 출구까지 수 많은 경로들이 존재할 수 있으며, 따라서 특정한경로 선택 기준(Path Selection Criteria)을 테스트 목표로 세우고 그에 따라 테스트 경로들을 선택하게 됨
  • 예를 들어, “소스 프로그램 내의 모든 문장(statements)이 커버되어야 한다가 테스트 목표로 정해졌다면, 그래프 내의 모든 노드가 적어도 한번씩은 테스트에서 실행되도록 경로를 선택하게 됨

 

) 아래 제어 흐름 그래프에 대해 테스트 목표를 100% 문장 커버리지(statement coverage)로 결정. 아래와 같은 두 개의 테스트 케이스를 작성하고 실행한다고 가정

테스트 케이스 #1: 1-2-exit

테스트 케이스 #2: 1-2-3-4-5-7-8-2-3-4-5-7-8-2-exit 

주어진 테스트 케이스만 가지고는 노드 6이 절대 실행되지 않으므로 테스트 목표를 달성하지 못하게 되며, 
따라서 노드 6을 커버할 수 있는 추가적인 테스트 케이스 생성이 요구됨

 

  • 프로그램의 제어 흐름 그래프에서의 경로 선택 기준은 테스트 커버리지 기준(a test coverage criterion)이라는 용어로도 불리며, 테스트 스위트(생성된 테스트 케이스들의 집합)가 테스트 대상 프로그램을 커버하는 정도를 측정하는 메트릭으로 사용된다.
  • 구조적 테스팅의 주요 장점 중 하나가 테스트 커버리지가 쉽고 정확하게 정의될 수 있다는 점이다.

 

 

제어 흐름 기반 테스트 커버리지 기준(Test Coverage Criteria)

다양한 제어 흐름 기반 커버리지 기준이 존재하며, 가장 대표적으로 아래를 들 수 있다.

  • 100% 문장 커버리지(statement coverage): 프로그램의 모든 문장들이 테스트에서 최소한 한 번씩은 실행되도록 경로를 선택한다. , 그래프의 모든 노드를 커버하는 테스트 집합을 찾는다. 데드 코드가 존재하는 경우 100% 문장 커버리지 달성이 어려움
  • 100% 브랜치 커버리지(branch coverage): 디시젼 커버리지로도 불리며, 프로그램의 모든 브랜치가 적어도 한 번씩은 테스트에서 실행되도록 한다. , 그래프의 모든 에지를 적어도 한번씩은 커버하는 테스트 짒합을 찾는다.
  • 100% 경로 커버리지(path coverage): 프로그램에 존재하는 모든 가능한 제어 흐름 경로가 한 번씩은 실행된다. 무한대의 실행 경로를 가지는 루프가 존재하거나 또는 실행해야 할 경로의 수가 너무 많기 때문에 대부분의 경우 현실적으로 달성 불가능한 목표

 

테스트 커버리지 기준들 간에는 포함 관계(subsumption relationship)가 존재하며, 따라서 아래와 같은 커버리지 계층도가 형성된다. 최하단의 기본이 되는 문장 커버리지(statement coverage)와 최상단의 가장 강력한 완전 경로 커버리지(all-paths coverage) 사이에 다양한 커버리지 기준이 존재하며, 상위에 위치한 커버리지 기준이 하위에 있는 기준을 포함하는 더 강력한 커버리지 기준이다. 예를 들어, 브랜치 커버리지가 문장 커버리지 보다 더 강력한 커버리지이며, 어떤 테스트케이스 집합이 브랜치 커버리지를 충족시킨다면 당연히 문장 커버리지도 충족시킨다. 하지만 그 역은 성립하지 않는다. 즉, 100% 문장 커버리지를 충족하는 테스트케이스 집합이 항상 100% 브랜치 커버리지를 충족하는 것은 아니다(대개 더 강력한 기준을 충족하려면 더 많은 수의 테스트케이스가 요구됨).

 

Note. 이 분야도 용어가 완전히 통일 된 것이 아니라서
위에 제시된 것과 좀 다르게 정의된 커버리지 기준들을 마주칠 수 있다(전체적인 맥락이나 원리는 동일함).
예를 들어, 아래는 다른 테스팅 강의에서 제시하는 커버리지 기준 포함관계도이다.

출처: 웹문서 https://sttp.site/ 2.3 구조적 테스팅(Structural testing)

 

테스트 적정성 기준(Test Adequacy Criteria)

  • 완벽한 테스팅이 불가능하므로 적정 수준의 테스트 케이스 샘플링을 할 수 밖에 없는 현실에서 우리 테스트가 충분한가?”, “지금 테스트를 중단해도 괜찮은가?”, “테스트가 충분하게 되었는지를 어떻게 알 수 있는가?” 라는 테스트 적정성(test adequacy)” 이슈가 생김
  • 테스트 커버리지 기준은 객관적으로 테스트 적정성을 판단해주고 언제 테스트를 멈추어도 되는지 기준이 되는 테스트 종료 기준(test stopping criteria)으로도 사용됨. 예를 들어, 단위 테스팅에서 소스 프로그램의 브랜치 커버리지를 품질 목표로 수립하였다면, 브랜치 커버리지를 충족시키는 테스트 케이스 집합이 설계되고 실행되었을 때 테스트 활동이 공식적으로 중단된다.
  • 기존 테스트 스위트의 적정성(품질)을 측정/평가하는데도 커버리지 기준이 사용될 수 있으며, 기존 테스트가 목표 기준에 미달하는 경우 어느 부분에 추가적인 테스트 케이스 생성이 필요한지(, 프로그램의 어떤 부분이 아직 테스트가 미흡한지) 안내해 주는 역할도 한다

 

테스트 커버리지 지원 툴

현실 프로젝트에서 수동으로 소스 코드의 테스트 커버리지 측정을 하는 것은 불가능하며, 따라서 커버리지 툴 사용이 필수적이다오늘날 대부분의 통합 개발 환경(IDE)이 커버리지 측정 기능을 포함하고 있고 다양한 커버리지 메트릭을 제공한다.

예를 들어 아래 영상은 Java 언어의 이클립스 개발환경에서 EclEmma 코드 커버리지 플러그인을 통해 자바 소스 코드의 테스트 커버리지를 측정하는 예를 보여준다(영상 길이: 3분10초). 

출처: Unit Testing in Java: JUnit & Mockito Tutorial for Beginners, Lectured by Bharath Thippireddy(인도)

 

 

반응형

+ Recent posts