|
오늘은 소프트웨어 테스팅에 대해서 간단하게라도 좀 적어볼려고 합니다. 예전글에서 이어지는 셈입니다.
소프트웨어 테스팅의 대가로 유명하신 저희 교수님의 책에 따르면 크게 4가지의 소프트웨어 테스팅 기준(Criteria)이 있다고 합니다.
1. Graph Coverage. 그래프 커버리지는 하나의 함수를 그래프 형태로 표현한 다음에 그 그래프 안에서 보여지는 모든 경로를 커버 할수 있도록 테스트 케이스를 작성하는 것입니다. 예를 들어 정수를 입력 받아서 그게 10진수로 2자리 숫자이면 true를 리턴하는 함수가 있다고 할때, 그 함수는 이런형태입니다. public boolean isTwoDecimal( int n )그러면 if문을 기준으로 분기가 발생하는데, 그 분기를 고려해서 그래프를 그리면 이런 형태가 됩니다. ![]() 1-2-3그러면 이렇게 얻어진 경로를 커버하는 테스트 케이스들을 만들면 되겠습니다. 이런식이 되겠습니다. assert( false == isTwoDecimal(0) );이런식으로 [프로그램]에서 [그래프]를 만들어내고, 그 그래프에서 [경로]를 얻어낸 다음에 [테스트 케이스]를 만들어서 모든 경로를 커버하는 방식이 Graph Coverage라고 합니다. 2. Logic Coverage 로직 커버리지도 그래프 커버리지랑 비슷한데, 그래프 얻는 대상이 조건문 안에 있는 Logic을 대상으로 합니다. 예를 들어 다음과 같이 10진수로 2자리 숫자인지를 확인하는 조건문이 있다고 할때 if( n < 10 && n >= 100 ) return false;로직 커버리지는 조건문 안에 있는 논리(Logic)에만 촛점을 둡니다. 여기에서 테이블이 얻어지는데, 다음과 같겠습니다.
이 테이블을 가지고 앞서와 동일한 3가지의 테스트 케이스를 작성할수 있겠습니다. 여기에서 논리적으로 불가능한 경우가 종종 등장하는데, 아직까지 수학적인 방법으로 불가능(Infeasible) 경우를 찾아내는 방법이 찾아지지 않았습니다. 3. Input Space Coverage. 이 방법은 전통적인 Black Box 방식으로 알려진 것과 비슷합니다. (좀 다른 점이 있지만 생략). 그래서 프로그램 코드를 읽지 않고, 그 [함수의 정의(Contract)]만 확인하고 테스트 케이스를 작성하는 방식입니다. 그래서 우리가 테스트 하려고 하는 함수가 "10진수로써 2자리이면 true를 리턴한다"라는 정의만 읽고 테스트 케이스를 작성하는 방식입니다. 이름이 Input Space 라고 불리는 이유는 다음과 같습니다. 주어진 함수 isTwoDecimal 의 입력 변수(argument)가 정수형(int)인 경우 입력 가능 범위는 -2^31 부터 2^31-1 까지 입니다. 이 많은 값을 일일이 다 넣어보고 결과를 확인하면 참 좋을것 같지만, 현실적으로 그런식의 테스트는 불가능합니다. 그래서 이 입력 범위(Input space)를 파티션(partition)합니다. 파티션이란, 하나의 덩어리를 여러개로 쪼게는데, 이 쪼갠 조각들의 합이 항상 전체와 같아야 합니다. 즉 남는 덩어리가 없어야 한다는 것입니다. 그리고 각 조각들이 서로 겹치지도 않아야 합니다. 근데 어떤식으로 파티션하느냐 하면, 해당 함수의 정의에 따라 파티션합니다. 예를 들어, 앞에서 사용된 isTwoDecimal의 예를 들면, 3개의 입력 범위로 나눌수 있습니다.
그리고 특수하게 잘 알려져 있는 값들, 예를 들면 0 라든지 null 값도 파티션의 대상에 포함해야합니다. 이 방식은 프로그래밍 언어에 독립적인데다가, 다른 테스팅 방식과는 달리 코딩을 하기 전에 설계 단계에 테스트 케이스 작성이 가능하다는 장점이 있습니다. 그래서 프로젝트 관리자에게 적합하다고 할수 있겠습니다. 4. Syntax Coverage 이방식이 사실 (개인적으로) 가장 흥미로운 방식입니다. 설명 하기에 따라 매우 길어질 수도 있습니다만 여기에서는 간단히 소개만 하겠습니다. Syntax Coverage는 소프트웨어를 테스트 할때에 해당 소프트웨어의 문법에 의존해서 테스트 하는 방식입니다. 예를 들어, 다음과 같은 간단한 프로그램이 있다고 할때, public int plusOne( int a )이것은 Java 문법에 맞는 프로그램이지요. 그런데 Java 문법에 맞으면서도 이런식의 오타를 입력했을수도 있습니다. public int plusOne( int a )즉, 더하기(+)를 했어야 하는데 프로그래머가 잘못 입력해서 (그러나 문법상으로는 맞는) 빼기(-)를 입력한 것입니다. 혹은 곱하기(*)나 나누기(/)를 했을수도 있고, 그 밖에도 문법적으로 가능한 어떤 실수를 했을수 있는 것이지요. Syntax Coverage는 이런 종류의 문제점, 즉 문법상으로는 맞으나 다른 동작을 하는 프로그램을 자동적으로 찾아내는 테스팅 방식입니다. 어떻게 이게 자동으로 될수 있는가? 우선, 테스팅 하고자 하는 소프트웨어가 구현이 되어있어야 합니다. 그리고 이 프로그램을 돌연변이(Mutate) 시켜서 돌연변이(Mutants) 프로그램들을 자동으로 만들어 냅니다. 앞서의 예를 들면 더하기(+)를 바르게 사용한 프로그램을 오리지널 프로그램으로 상정하고, 뮤테이팅 프로그램을 사용해서 그 오리지널을 돌연변이 시켜서 여러개의 돌연변이 프로그램들을 자동으로 생성합니다. 이 돌연 변이 프로그램들은 문법적으로는 맞지만, 한가지씩 바꿔치기 되어있는 프로그램들입니다. 그러면 이제 오리지널 프로그램들을 테스트 하는 테스팅 케이스들을 (어떤 방식으로든지) 만들어 냅니다. 우리가 기대 하는 것은 이 테스팅 케이스들이 오리지널 프로그램에서 출력시키는 결과값과 돌연변이 프로그램들에서 리턴하는 결과같이 반듯이 달라지기를 기대하는 것입니다. 왜냐하면 돌연변이 프로그램들은 오리지널 프로그램에서 파생되어 나왔지만 1가지씩 오류를 포함하고 있기 때문에 오리지널과는 다르게 동작하기 때문입니다. 여기에서 우리가 최종적으로 얻을수 있는 결과물은, 효율적인 테스트 케이스들입니다. 예를 들어, 테스트 케이스가 (위의 예를 기준으로) 1이 선택되었습니다. 그러면 오리지널 프로그램은 1 + 1 해서 2 를 리턴 할 것입니다. 그런데 만약 돌연변이 프로그램이 1 * 1을 한다면 결과 값으로 1 을 리턴할 것이기 때문에 이 테스트 케이스는 프로그래머의 오타 입력 실수를 찾아내는데 사용되기에 효율적인 테스트 케이스로 인정되는 것입니다. 만약 여기에서 테스트 케이스로 0 이 선택되고 돌연변이 프로그램이 오타로 2진 논리연산자 OR (자바에서는 | 이지요) 를 사용했다면, 오리지널 프로그램도 1을 리턴할 것이고, 돌연변이 프로그램도 1을 리턴할 할것입니다. 그러면 이 테스트 케이스(입력값 0)은 효율성이 떨어지는 것으로 평가 되는 것입니다. ![]() | ||||||||||||||||||||||||