본문 바로가기

4.개발 및 운영 환경

[CUnit] C에서 Unit test 하기

들어가기

Unit test 필요성을 느끼면서 어떤 툴을 사용할지 고민이다.

예전에 C++에서 CPPUnit테스트를 사용했었는데, VC환경에서 호환이 잘 안되는 문제점이 있었다.

완전히 안되는 것이 아니고 GUI와 호환성 문제가 있었다.

작성자: http://ospace.tistory.com/(ospace114@empal.com) 2008.12.31

최근 C언어를 사용하는 프로젝트를 진행하면서 테스터 프로그램을 선택하기 위해 구글링을 해보았다.
최근에 이슈되는 것 중에 GTest가 있었다.

URL: http://code.google.com/p/googletest/

대부분 사용하는 사람들이 공통적으로 개발자를 생각하였다. 잘 만들어졌다라고 한다. 그러나 C++용이다.
이건 나중에 살펴보고 C용 unit test를 찾아보았다. 그래서 찾은 것이 CUnit이다.

설치하기

아직 빌드는 하지 못했다. 생각처럼 빌드가 잘 안된다. 윈도우 환경이라서 configure를 사용할 수 없어 cygwin를 설치해서 해보는데도 잘 안된다.

설치 순서는

$ configure
$ make

끝이다. 근데 configure가 아애 없다. 그래서 autoconf하면 생긴다고 한다. 했다. 에러가 발생했다.

$ autoconf
' is already registered with AC\_CONFIG\_FILES.
/usr/src/packages/autoconf/26/autoconf2.5-2.63-1/src/autoconf-2.63/lib/autoconf/
status.m4:305: AC\_CONFIG\_FILES is expanded from...
configure.in:241: the top level
autom4te-2.63: /usr/bin/m4 failed with exit status: 1

configure도 없다. 그래서 안돼서 jam으로 시도했다.

C:\\...\\CUnit-2.1-0>jam
Compiler is GCC with Mingw
...found 8 target(s)...

끝이다. 아무것도 한게 없다. 이게 뭥미? ㅡ.ㅡ;
나의 무식함... OTL

다시 자료 수집 후에 autoconf전에 aclocal을 실행해줘야 한다.

  1. aclocal: 의해서 기본 사용 툴 정보를 수집해서 aclocal.m4 파일에 저장하고
  2. autoconf: configure.in에서 configure을 생성한 후에
  3. ./configure: 로 기본 Makefile을 생성한다.
  4. make: 빌드한다.

순서는 이런 것 같은데 안된다. ㅡ.ㅡ;

다른 자료에 따르면 자동 빌드 환경 생성 기본적인 순서는 다음과 같다.

  1. makefile.am를 작성
  2. configure.in를 작성
  3. autoscan를 이용해서 템플릿 생성
  4. 생성된 configure.scan 템플릿 파일을 자신 프로젝트에 맞게 수정
  5. configure.scan를 configure.in으로 변경
  6. automake 실행해서 makefile.am에서 makefile.in를 생성
  7. aclocal로 autoconf의 모드 매크로를 로컬에 복사본 생성. 이 파일을 프로젝트에서 사용함.
  8. autoconf를 실행해서 configure를 생성.

그럼 makefile.am내용을 보자...(헉) OTL
이건 RPM 패키징하는 코드다..
물론 컴파일도 될지도 모르지만, 지금 필요한건 컴파일해서 설치하는 것이다.
결국, 드디어 해결했다. 너무 허무한 순간....
소스 다운 받을때, 두가지 압축 파일이 있다.

CUnit-2.1-0-src.tar.gz  
CUnit-2.1-0-src.zip

여기서 윈도우에 사용한다고 zip를 받으면 안된다. 받아서 설치하면 위와 같이 헛짓한다.

중요!!!

CUnit-2.1-0-src.tar.gz를 다운받음. 절대 CUnit-2.1-0-src.zip은 안됨.

설치는 간단하다.

$ configure
$ make
$ make install

필요시 설치되는 위치를 변경하려면 configure에서 --prefix로 설치 위치를 지정하면 된다.

기본 설치하면, 아래와 같은 폴더에 설치된다.

  • Include 파일: /usr/local/include/CUnit
  • 문서 파일: /usr/local/doc/CUnit
  • man파일: /usr/local/man/man3
  • 공유파일: /usr/local/share/CUnit
  • lib파일: /usr/local/lib/libcunit.a와 libcunit.la

사용하기

실제 예제 코드를 이용해서 살펴보겠다.

아래 파일 명을 cunit1.c라고 하자.

/*
 *  Simple example of a CUnit unit test.
 *
 *  This program (crudely) demonstrates a very simple "black box"
 *  test of the standard library functions fprintf() and fread().
 *  It uses suite initialization and cleanup functions to open
 *  and close a common temporary file used by the test functions.
 *  The test functions then write to and read from the temporary
 *  file in the course of testing the library functions.
 *
 *  The 2 test functions are added to a single CUnit suite, and
 *  then run using the CUnit Basic interface.  The output of the
 *  program (on CUnit version 2.0-2) is:
 *
 *           CUnit : A Unit testing framework for C.
 *           http://cunit.sourceforge.net/
 *
 *       Suite: Suite_1
 *         Test: test of fprintf() ... passed
 *         Test: test of fread() ... passed
 *
 *       --Run Summary: Type      Total     Ran  Passed  Failed
 *                      suites        1       1     n/a       0
 *                      tests         2       2       2       0
 *                      asserts       5       5       5       0
 */

#include <stdio.h>
#include <string.h>
#include "CUnit/Basic.h"

/* Pointer to the file used by the tests. */
static FILE* temp_file = NULL;

/* The suite initialization function.
 * Opens the temporary file used by the tests.
 * Returns zero on success, non-zero otherwise.
 */
int init_suite1(void)
{
   if (NULL == (temp_file = fopen("temp.txt", "w+"))) {
      return -1;
   }
   else {
      return 0;
   }
}

/* The suite cleanup function.
 * Closes the temporary file used by the tests.
 * Returns zero on success, non-zero otherwise.
 */
int clean_suite1(void)
{
   if (0 != fclose(temp_file)) {
      return -1;
   }
   else {
      temp_file = NULL;
      return 0;
   }
}

/* Simple test of fprintf().
 * Writes test data to the temporary file and checks
 * whether the expected number of bytes were written.
 */
void testFPRINTF(void)
{
   int i1 = 10;

   if (NULL != temp_file) {
      CU_ASSERT(0 == fprintf(temp_file, ""));
      CU_ASSERT(2 == fprintf(temp_file, "Q\n"));
      CU_ASSERT(7 == fprintf(temp_file, "i1 = %d", i1));
   }
}

/* Simple test of fread().
 * Reads the data previously written by testFPRINTF()
 * and checks whether the expected characters are present.
 * Must be run after testFPRINTF().
 */
void testFREAD(void)
{
   unsigned char buffer[20];

   if (NULL != temp_file) {
      rewind(temp_file);
      CU_ASSERT(9 == fread(buffer, sizeof(unsigned char), 20, temp_file));
      CU_ASSERT(0 == strncmp(buffer, "Q\ni1 = 10", 9));
   }
}

/* The main() function for setting up and running the tests.
 * Returns a CUE_SUCCESS on successful running, another
 * CUnit error code on failure.
 */
int main()
{
   CU_pSuite pSuite = NULL;

   /* initialize the CUnit test registry */
   if (CUE_SUCCESS != CU_initialize_registry())
      return CU_get_error();

   /* add a suite to the registry */
   pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1);
   if (NULL == pSuite) {
      CU_cleanup_registry();
      return CU_get_error();
   }

   /* add the tests to the suite */
   /* NOTE - ORDER IS IMPORTANT - MUST TEST fread() AFTER fprintf() */
   if ((NULL == CU_add_test(pSuite, "test of fprintf()", testFPRINTF)) ||
       (NULL == CU_add_test(pSuite, "test of fread()", testFREAD))) {
      CU_cleanup_registry();
      return CU_get_error();
   }

   /* Run all tests using the CUnit Basic interface */
   CU_basic_set_mode(CU_BRM_VERBOSE);
   CU_basic_run_tests();
   CU_cleanup_registry();
   return CU_get_error();
}

컴파일은 다음과 같다.

gcc cunit1.c -o cunit1.exe -L/usr/local/lib -lcunit

위는 cygwin에서 컴파일하다는 가정이다. 만약 linux라면 "-o cunit1"으로 설정하면 될 것이다.

이를 makefile로 작성해서 매번 귀찮은 명령입력을 간편하게 만들자.

.SUFFIXES : .c .o
RM := rm -rf
CC := gcc
CFLAGS :=  -g -Wall
INCLUDES := -I/usr/local/include
LIBS := -L/usr/local/lib -lcunit
# All Target
all: cunit1.exe

# Test Tool
cunit1.exe: cunit1.o 
$(CC) -o $@ $^ $(LIBS) 

%.o: %.c
$(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@

# Clean
clean: 
-$(RM) cunit1.exe cunit1.o
-@echo ' '

makefile 조금만 다뤄보았다면 쉽게 이해할거라 생각한다. 만약 잘 모른다면, 이번 기회에 makefile 조금 공부해보길 바란다.

Troubleshoot

아래 에러는 여기서 해당되지 않는다. CUnit-2.1-0-src.zip에서 발생한 에러인데 CUnit-2.1-0-src.tar.gz.gz를 사용하게 되면 아래와 같은 문제는 발생하지 않는다.

autoconf 실행시

' is already registered with AC\_CONFIG\_FILES.  
autom4te-2.63: /usr/bin/m4 failed with exit status: 1  
\=>configure.in이나 configure.ac 파일에 캐리지리턴을 unix 호환으로 변경

automake 실행시

error: possibly undefined macro: AC\_PROG\_LIBTOOL  
\=> libtool이 설치되었는지 확인

./configure 실행시

configure: error: cannot run /bin/sh ./config.sub  
\=> unknown

실제 프로그램 실행시

에러: ./cunit1.exe: error while loading shared libraries: libcunit.so.1: cannot open shared object file: No such file or directory

해결: LD_LIBRARY_PATH설정

Linux의 bash인 경우, .bashrc의 적당한 장소에 넣고, CUnit의 라이브러리 파일이 /home/myid/local/lib에 있다면

LD\_LIBRARY\_PATH=/home/myid/local/lib:$LD\_LIBRARY\_PATH  
export LD\_LIBRARY\_PATH

을 추가하면 된다.

Reference

CUnit참조

http://cunit.sourceforge.net/index.html

http://cunit.sourceforge.net/doc/index.html

http://blog.naver.com/yuzico/130017038508

http://galaxyra.linuxstudy.pe.kr/galaxyra/14

http://edge77.egloos.com/1423103

GTest참조

http://code.google.com/p/googletest/

http://yeast25.tistory.com/tag/gtest

GNU Build Tool

http://developers.sun.com/solaris/articles/gnu.html

http://www.gnu.org/software/autoconf/manual/autoconf.html

테스트 케이스 관련 사이트

http://selab.cu.ac.kr/Data/lecture/student/Software_Engineering/2000fall/lec/chap16.html

tool 관련 사이트

http://www.testingfaqs.org/t-unit.html

http://cunit.sourceforge.net/

반응형

'4.개발 및 운영 환경' 카테고리의 다른 글

Socket에서 select사용하기  (2) 2009.01.15
Bash shell coloring  (0) 2009.01.05
도스창 팁: Command Prompt Tip  (0) 2008.12.24
프로그래밍용 폰트  (0) 2008.12.11
[FreeMind] Flash Browser Testing  (0) 2008.12.10