통합 테스트의 개념


단위 테스트가 끝난 모듈을 통합하는 과정에서 발생할 수 있는 오류를 찾는 테스트가 통합 테스트(integration test)이다. 실제 업무에서는 단위 모듈이 개별적으로 존재하는 것이 아니고 여러 모듈이 유기적 관계를 맺고 있으므로 이러한 모듈들을 결합한 형태로 테스트를 수행해봐야 한다. 이때 주로 확인하는 것은 '모듈 간의 상호작용이 정상적으로 수행되는가'이다. 즉 모듈 사이의 인터페이스 오류는 없는지, 모듈이 올바르게 연계되어 동작하고 있는지를 체크한다.


개별 모듈을 테스트하는 단위 테스트에서는 오류가 발견되지 않았어도, 모듈을 통합하면 상호 간의 인자나 공유 데이터 구조 등에서 오류가 발생할 수 있다. 또 단위 테스트 시 가상의 드라이버와 스텁 모듈을 만들어 테스트를 잘 수행했더라도, 상당히 제한적인 여건에서 테스트를 수행한 것이다. 그러므로 실제 모듈 통합 시에는 다른 결과가 나올 수도 있다. 실제 개발에서는 모듈 간의 상호작용과 인터페이스에서 많은 오류가 발생하는 것을 볼 수 있다. 그러므로 통합 테스트가 필요한데, 그 방법에는 모듈 통합을 한꺼번에 하는 방법과 점진적으로 하는 방법이 있다.


모듈 통합을 한꺼번에 하는 방법으로는 빅뱅(big-bang) 테스트를 들 수 있다. 빅뱅 테스트는 단위 테스트가 끝난 모듈을 한꺼번에 결합하여 수행하는 방식이다. 이 방법은 소규모 프로그램이나 프로그램의 일부를 대상으로 하는 경우가 많고 그만큼 절차가 간단하고 쉽다. 그러나 한꺼번에 통합하면 오류가 발생했을 때 어떤 모듈에서 오류가 존재하고 또 그 원인이 무엇인지 찾기가 매우 어렵다.


모듈 통합을 점진적으로 하는 방법은 모듈 통합을 한꺼번에 하는 방법의 문제를 극복하는 방법으로, 완성된 모듈을 기존에 테스트된 모듈과 하나씩 통합하면서 테스트한다. 문제가 발생하면 바로 직전에 통합하여 테스트한 모듈에서 오류가 발생했다고 짐작할 수 있으므로 오류를 찾기가 쉽다. 점진적 통합 방식은 가장 상위에 있는 모듈부터 테스트하는지, 가장 하위에 있는 모듈부터 테스트하는지에 따라 하향식 기법과 상향식 기법으로 나뉜다.



1. 점진적 모듈 통합 방법 : 하향식 기법


하향식(top-down) 기법은 시스템을 구성하는 모듈의 계층 구조에서 맨 상위의 모듈부터 시작하여 점차 하위 모듈 방향으로 통합하는 방법이다.


[그림 8-24]의 예에서 맨 상위 모듈 A를 모듈 B와 통합하여 테스트한다. 그다음으로 모듈 C를 먼저 선택할지, 아니면 B 아래에 있는 모듈 E를 먼저 선택할지 결정해야 한다. 이때 모듈 C를 먼저 선택하는 방식을 넓이 우선(breadth first) 방식이라 하고 모듈 E를 먼저 선택하는 방식을 깊이 우선(depth first) 방식이라 한다.

넓이 우선 방식으로 테스트하면 (A, B)→(A, C)→(A, D)→(A, B, E)→(A, B, F)→(A, C, G)와 같이 같은 행에서는 옆으로 가며 통합 테스트를 한다. 반면, 깊이 우선 방식에 따라 테스트하면 (A, B)→(A, B, E)→(A, B, F)→(A, C)→(A, C, G)→(A, D) 순으로 통합하며 테스트한다. 즉 하위 방향으로 내려가면서 통합하지만, 같은 행에서는 옆이 우선이 아니라 그 아래 모듈을 먼저 통합하여 테스트한다. 또한 하향식 방식에서는 상위 모듈부터 테스트를 수행하기 때문에 단위 테스트에서 설명한 스텁 모듈이 필요하다.


                         <그림 8-24 점진적 모듈 통합 방법을 설명하기 위한 모듈 구성도>


일반적으로 모듈의 종속 관계에서 상위 모듈은 시스템 전체의 흐름을 관장하고, 하위 모듈은 각 기능을 구현하는 형태로 구성되어 있다. 그러므로 하향식 기법을 이용하면 프로그램 전체에 영향을 줄 수 있는 오류를 일찍 발견하기가 쉽다. 그러나 하위 모듈이 임시로 만든 스텁들로 대체되어 결과가 완전하지 않을 수도 있고 스텁 수가 많으면 스텁을 만드는 데 시간과 노력이 많이 들 수 있다. 따라서 모듈 간의 인터페이스와 시스템의 동작이 정상적으로 잘되고 있는지를 빨리 파악하고자 할 때 하향식 기법을 사용하는 것이 좋다.



2. 점진적 모듈 통합 방법 : 상향식 기법


상향식(bottom-up) 기법은 하향식 기법과는 반대로 가장 말단에 있는 최하위 모듈부터 테스트를 시작한다. 하향식에서 스텁이 필요했다면, 상향식에서는 상위 모듈의 역할을 하는 테스트 드라이버가 필요하다. 이 드라이버는 하위 모듈을 순서에 맞게 호출하고, 호출할 때 필요한 매개 변수를 제공하며, 반환 값을 전달하는 역할을 한다.

[그림 8-24]에서 상향식 기법을 이용하여 테스트하는 순서는 다음과 같다. 우선 가장 말단(레벨 3)에 있는 모듈 E와 F를 모듈 B에 통합하여 테스트한다. 그다음 모듈 G를 모듈 C에 통합하여 테스트한다. 마지막으로 모듈 B, C, D를 모듈 A에 통합하여 테스트한다.

상향식 기법의 장점은 최하위 모듈들을 개별적으로 병행하여 테스트할 수 있기 때문에 하위에 있는 모듈들을 충분히 테스트할 수 있다는 것이다. 또한 정밀한 계산이나 데이터 처리가 요구되는 시스템 같은 경우에 사용하면 좋다. 그러나 상위 모듈에 오류가 발견되면 그 모듈과 관련된 하위의 모듈을 다시 테스트해야 하는 번거로움이 생길 수 있다.

출처 : 쉽게 배우는 소프트웨어 공학


Controller Tester 에서의 통합테스트 방법 


#include <stdio.h>

int a, b, result;

void func_A(void)
{
  func_B();
  func_C();
  func_D();
}

void func_B(void)
{
  func_E();
  func_F();
}
void func_C(void)
{
  func_G();
}
void func_D(void)
{
  printf("");
}
void func_E(void)
{
  printf("");
}
void func_F(void)
{
  printf("");
}
void func_G(void)
{
  printf("");
}

<예제 소스파일 test.c>



1. 전체 테스트

[그림 8-24]와 같은 형태를 갖도록 구성된 예제소스(test.c)에서 func_A() 부터 func_G() 까지 모든 함수의 구현이 완료되어 전체 테스트를 진행한다고 가정한다.

이 경우에는 굳이 Controller Tester의 [통합 테스트]기능을 사용하지 않고, [유닛 테스트]에서도 통합 테스트가 가능하다.

일반적인 유닛테스트에서는 다른 함수들의 동작은 고려하지 않고, 테스트 대상이 되는 하나의 함수에 대한 테스트만을 개별로 진행하기 때문에, 테스트 대상 함수 이외의 함수들은 스텁처리하여 유닛테스트를 수행한다.

만약 func_A() 함수에 대한 유닛테스트를 생성하고, 이외의 함수에 대한 스텁처리를 하지 않으면, func_A() 함수 안에서 이루어지는 다른 함수의 호출과정을 모두 수행하게 된다.

따라서 [유닛 테스트]에서 func_A() 함수에 대한 유닛테스트를 생성하고, 다른 함수들에 대해 별도의 스텁처리를 하지 않는다면, 실제 함수들의 호출관계가 이루어지는 전체 테스트가 가능해지고, 아래와 같이 하나의 유닛테스트로 프로젝트의 전체 테스트가 수행된다.


물론 이상태로 보고서를 출력하게 되면, 통합테스트를 수행했음에도 [유닛 테스트] 항목으로 표시되기 때문에, 필요에 따라 [통합 테스트]에 해당 유닛테스트를 복사하여 수행하거나, [통합 테스트]뷰에서 func_A()함수에 대한 테스트를 생성하여 수행해도 동일한 결과를 얻을 수 있다.




2. 점진적 - 하향식

상위 모듈부터 기능구현이 완료되어 하향식 테스트를 진행하며, [그림 8-24]에서 A, B만을 통합하여 테스트한다고 가정한다.

func_A() 함수에 대한 유닛테스트를 생성하고, 테스트 대상에서 제외할 C, D, E, F에 대해 스텁처리하면, func_A() 함수와 func_B()에 대한 통합테스트만을 진행할 수 있다.

(여기서 func_G()의 경우, func_C()에 의해서만 호출되는 관계이기 때문에 스텁처리를 하지 않아도 된다.)


이 경우에도, [1. 전체 테스트] 에서와 마찬가지로, [유닛 테스트]만으로도 통합 테스트를 구현할 수 있으며, 필요한 경우, [통합 테스트]에 해당 유닛테스트를 복사하여 수행하거나, [통합 테스트]뷰에서 테스트를 생성하여 수행해도 동일한 결과를 얻을 수 있다.



만약 [그림 8-24]에서 A, C, G 만을 통합하여 테스트를 진행한다면, 위와 마찬가지로 func_A()에 대한 유닛테스트를 생성하고 B, D에 대해 스텁처리하여 수행할 수 있다.

위 에서와 마찬가지로 E, F의 경우에도 B D에 의해서만 호출되는 구조이기 때문에 스텁처리를 하지 않아도 되며, [통합 테스트]에 유닛테스트를 복사하거나, [통합 테스트]뷰에서 테스트를 생성하여 수행해도 결과는 동일하다.




3. 점진적 - 상향식

하위 모듈부터 기능구현이 완료되어 상향식 테스트를 진행하며, [그림 8-24]에서 B, F만을 통합하여 테스트한다고 가정한다.

여기서의 테스트 구성법 또한, [2. 점진적 - 하향식] 에서와 크게 다르지 않다.

func_B() 함수에 대한 유닛테스트를 생성하고, 테스트 대상에서 제외할 E 에 대해 스텁처리하면, func_B() 함수와 func_F()에 대한 통합테스트만을 진행할 수 있다.

여기서도 위에서 보았던 예제처럼, func_B() 함수에 의해서는 호출되지 않는 A, C, D, G 에 대해서는 스텁처리를 하지 않아도 되며, [통합 테스트]에 유닛테스트를 복사하거나, [통합 테스트]뷰에서 테스트를 생성하여 수행해도 결과는 동일하다.




4. 점진적 - 상향식(호출순서 기준)

하위 모듈부터 기능구현이 완료되어 상향식 테스트를 진행하며, [그림 8-24]에서 A만을 제외한 B, C, D, E, F, G를 통합하여 테스트 하되, 실제 프로그램의 시나리오에 의한 실행순서(B->C->D)를 고려하여 테스트를 진행한다고 가정한다.

main() 함수에 해당하는 func_A()의 구현이 아직 완료되지 않아, 전체 테스트는 불가능하지만, func_A()의 구현이 완료된다면, func_A()에 의해 B->C->D 순으로 호출된다고 가정할 때, [통합 테스트]에서 원하는 시나리오대로 테스트를 구성할 수 있다.
[통합 테스트]뷰에서 [생성]버튼을 눌러, 통합 테스트를 생성하고, 해당 테스트의 이름에 마우스 우클릭 - [테스트 생성]을 선택하여 통합 테스트에 포함될 유닛 테스트들을 생성한다.



테스트를 생성할 대상 함수들을 선택한다. 여기서는 A만을 제외한 B, C, D, E, F, G를 통합하여 테스트 한다는 가정이지만, 최하위 모듈인 E, F, G의 경우, 상위 모듈인 B, C, D에 의해서 호출될 예정이기 때문에 func_B(), func_C(), func_D()에 대한 테스트만 선택하고 [다음]을 선택한다.



다음으로 입력 및 출력을 제어할 전역변수를 선택하는데, 예제 소스에서는 해당되지 않지만, a, b 변수가 전역에 선언되어 있고, A모듈에서 a, b의 값을 설정하여 하위 모듈들을 실행하면, A를 제외한 하위 모듈들에 의해 그 값이 변경 및 참조되며, 그 결과값이 result 전역변수에 저장된다고 가정하고, 입력 제어를 위한 a, b 그리고 출력 제어를 위한 result 변수를 선택했다.



전역변수 선택을 마치고 [완료]버튼을 누르면, 아래 그림과 같이 B, C, D모듈에 대한 유닛 테스트가 생성되고, 가장 상단에 소스코드의 이름을 가진, 내가 선택한 전역변수를 제어할 수 있는 테스트가 생성된다.



[통합 테스트]뷰의 사용법은 전역변수의 입력값을 세팅하고, 원하는 테스트들을 순차적으로 수행한 후에, 각 테스트들에 의해 변경된 전역변수의 출력값을 최종 확인하는 방식이다. 이 과정은 일반적으로 main()함수에서 담당하는 역할이며, 예제소스에서는 A모듈(func_A)의 역할에 해당된다.

하지만 A모듈이 테스트 대상에 포함되어 있지 않기 때문에 입력값 세팅, 모듈 호출, 출력값 확인 까지의 과정을 담당하는, 즉 main()함수의 역할을 대신 수행해줄 통합 테스트가 필요한데, 위 그림에서 [INTEGRATION_0] 라는 이름을 가진 통합테스트가 main()함수의 역할을 대신하게 된다.

이제 위에서 언급한 시나리오대로 통합 테스트를 구성한다. 먼저, 필요한 전역변수에 입력값을 제어하기 위해 위 그림에서 [test.c_0]라는 이름을 가진 테스트를 편집하는데, 테스트 구조의 가독성을 높이기 위해 [F2]키를 이용해서 해당 테스트의 이름을 [input]으로 변경하고, [테스트 정보]탭에서 필요한 전역변수의 입력을 활성화하여, [테스트 케이스]탭에서 필요한 입력값을 세팅한다.



다음으로 테스트 시나리오에 따라, 각 모듈의 호출 순서에 맞게 테스트를 배치한다. [통합 테스트]뷰에서의 테스트 수행 순서는 위에서 아래로 진행된다. 앞에서 가정한 순서에 따라 [통합 테스트]뷰에서 Drag & Drop, 혹은 화살표 버튼을 이용하여 B, C, D 순으로 정렬한다.

A모듈의 경우, 테스트 대상이 되는 하위 모듈에 의해 피호출되지 않는 구조이며, A모듈을 제외한 다른 모듈들은 모두 실제 호출이 이루어져야하는 테스트 대상이기 때문에 결과적으로 어떤 함수에 대한 스텁도 필요하지 않다.



마지막으로 전역변수의 최종값(출력값)을 확인하기 위해 입출력 제어가 가능한 테스트를 추가해야 한다. 그림에서 [INTEGRATION_0] 내부에 테스트를 추가해도 되고, [input]으로 설정한 테스트를 복사하여 붙여넣기를 선택해도 된다. 이렇게 생성된 테스트의 이름도 가독성을 위해 이름을 [output] 으로 변경하고 [테스트 정보]탭에서 필요한 전역변수의 출력값을 활성화하여, [테스트 케이스]탭에서 기대값을 입력한다.



이렇게 구성이 완료된 통합 테스트를 수행하면 아래의 그림과 같이, func_A()함수를 제외한 모든 함수들이 수행된 것을 확인할 수 있다.



아래의 그림처럼 [output]테스트의 [테스트 케이스]탭에서 사용자가 입력한 기대값과 실제 테스트의 수행 결과에 의한 출력값을 확인할 수 있다.