简述
关于C下的单元测试框架有许多,而且大多数比较复杂。我在写CTMemory的时候比较了好多框架,最终还是选择了greatest。原因如下:
- 非常轻量,引入一个头文件即可。
- 功能相对完善,支持测试用例用套件的方式进行组织,生成的测试程序可带各种参数进行配置。
- 使用方便,没多少函数,看一下样例一会儿就明白了。
开始
去https://github.com/silentbicycle/greatest把代码拉下来就好。
先写一个makefile,方便跑测试
用greatest做单元测试,本质上就是使用greatest提供的宏来写一个程序,这个程序去跑一个又一个的TEST函数。所以在写这个程序之前,我们最好先写一个makefile方便后面的工作。
test.run
就是编译生成的程序。test_main.c
就是你的测试程序的main函数写的地方,同时也负责组织test suittest_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加载进去。
最后附上邮件原文:
Comments
comments powered by Disqus