简述



关于C下的单元测试框架有许多,而且大多数比较复杂。我在写CTMemory的时候比较了好多框架,最终还是选择了greatest。原因如下:


  1. 非常轻量,引入一个头文件即可。
  2. 功能相对完善,支持测试用例用套件的方式进行组织,生成的测试程序可带各种参数进行配置。
  3. 使用方便,没多少函数,看一下样例一会儿就明白了。




开始



https://github.com/silentbicycle/greatest把代码拉下来就好。



先写一个makefile,方便跑测试


用greatest做单元测试,本质上就是使用greatest提供的宏来写一个程序,这个程序去跑一个又一个的TEST函数。所以在写这个程序之前,我们最好先写一个makefile方便后面的工作。


  • test.run就是编译生成的程序。
  • test_main.c就是你的测试程序的main函数写的地方,同时也负责组织test suit
  • test_cases.c就是你写test case的地方。
  • fileToTest.c就是你要测试的模块。


关于如何写makefile,可以看这里


CFLAGS += -Wall -Werror -Wextra -pedantic
CFLAGS += -Wmissing-prototypes
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wmissing-declarations

CC = clang

HEADER_PATH = -I./greatest/

LINKED_OBJECTS = test_cases.o fileToTest.o

all: clean test.run

test: clean test.run
    ./test.run -v | ./greatest/greenest  # greatest提供了greenest这个程序来把通过的标绿,不通过的标红。

test.run: test_main.c ${LINKED_OBJECTS}
    ${CC} ${HEADER_PATH} ${CFLAGS} ${LDFLAGS} -g -o $@ test_main.c ${LINKED_OBJECTS}

clean:
    rm -f *.o *.core *.out *.run

test_cases.o:
    ${CC} ${HEADER_PATH} ${CFLAGS} ${LDFLAGS} -g -o $@ -c test_cases.c

fileToTest.o:
    ${CC} ${HEADER_PATH} ${CFLAGS} ${LDFLAGS} -pthread -g -o $@ -c ../fileToTest.c


这样写好以后每次make test就好了。



写test_main.c


这时候先不着急去写具体的实现代码,我们测试先行。文件名不重要,我喜欢叫test_main.c,你可以按照你自己的来。

在greatest框架中,有SUITE的概念,一个SUITE里面有很多个TEST。假设我们要测试A模块,那么就设立一个A SUIT,然后这个A SUIT里面都是关于A模块的TEST。对于test_main.c来说,只要组织好SUIT就好了,具体各自模块的TEST由各自的SUIT来组织。这样的管理方式非常棒,多人写测试代码的时候,各自管好各自的SUIT就好了。我的习惯是一个SUIT对应一个C文件,这样找代码的时候也比较好找。


下面的代码是test_main.c的:

  #include <stdlib.h>
  #include <stdio.h>
  #include <assert.h>

  #include "greatest.h"

  extern SUITE(test_cases);  /* test_cases这个SUIT是在test_cases.c里面的,在makefile里面已经链接进去了 */
  extern SUITE(test_cases2);

  GREATEST_MAIN_DEFS(); /* 这句必须要有,这个宏里面有greatest相关函数的定义 */

  int main(int argc, char **argv)
  {
      GREATEST_MAIN_BEGIN();

      RUN_SUITE(test_cases);
      RUN_SUITE(test_cases2);

      GREATEST_MAIN_END();

      return 0;
  }



写test_cases.c


test_cases.c其实就是一个SUIT,这个SUIT里面有很多TEST。我们直接看代码:


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "greatest.h"
#include "fileToTest.h"

SUITE(test_cases);  /* 在这里声明一下SUITE */

TEST test_operatin1(void) /* 每一个test case 的函数类型都用TEST来声明 */
{
    ASSERT(true);
    PASS();
}

TEST test_operatin2(void)
{
    ASSERT(true);
    PASS();
}

TEST test_operatin3(void)
{
    ASSERT(true);
    PASS();
}

SUITE(test_cases) {
    RUN_TEST(test_operatin1); /* 这里决定了都要跑哪些TEST */
    RUN_TEST(test_operatin2);
    RUN_TEST(test_operatin3);
}




结束

greatest是一个很小很方便的单元测试框架,但目前它还不支持多线程环境下的单元测试。一般我们在写自己的开源项目的时候,没有QA工程师来给你人肉测,只能靠我们自己去写测试用例了,greatest最适合的就是这样的场景。




2015-5-12 补:关于多线程下单元测试的处理

今天早上收到了greatest作者的邮件,他说在多线程的情况下,可以使用他写的另一套工具https://github.com/silentbicycle/autoclave来做测试。

他还补充了一点:greatest设计的初衷是单独测试程序的某一个部分,这时候默认是不考虑多线程的不确定性的。然而autoclave会一直跑你的程序,直到出现问题,才会停,这时候比如当程序出现死锁,它就能够帮你把debugger加载进去。

最后附上邮件原文:
mail








评论系统我用的是Disqus,不定期被墙。所以如果你看到文章下面没有加载出评论列表,翻个墙就有了。




本文遵守CC-BY。

请保持转载后文章内容的完整,以及文章出处。本人保留所有版权相关权利

如果您觉得文章有价值,可以通过支付宝扫描下面的二维码捐助我。


Comments

comments powered by Disqus