简版C语言测试框架实现

简版C语言测试框架实现


一、什么是单元测试框架?

对于单元测试框架来讲,它主要完成以下几件事。

提供用例组织与执行:测试用例只有几条时,可以不考虑用例组织,但是用例达到成百上千时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问题

提供丰富的断言方法:不论是功能测试,还是单元测试,在用例执行完之后都需要将实际结果与预期结果相比较(断言),从而断定用例是否执行通过。单元测试框架一般提供丰富的断言方法。例如:判断相等/不等、包含/不包含、True/False的断言方法等

提供丰富的日志: 当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等。

从这些特性来看单元测试框架的作用是:帮助我们更自动化完成测试,所以,它是自动化测试的基础。

接下来我们对照gtest测试框架实现一个自己的简化版测试框架。

二、代码

test.h
#ifndef __test_H__
#define __test_H__

#include <stdio.h>
#include "linklist.h"

//添加输出字体颜色
#define COLOR(str, color) "\033[" #color "m" str "\033[0m"
#define COLOR_HL(str, color) "\033[1;" #color "m" str "\033[0m"

#define RED(str) COLOR(str,31)
#define GREEN(str) COLOR(str,32)
#define YELLOW(str) COLOR(str,33)
#define BLUE(str) COLOR(str,34)

#define RED_HL(str) COLOR_HL(str,31)
#define GREEN_HL(str) COLOR_HL(str,32)
#define YELLOW_HL(str) COLOR_HL(str,33)
#define BLUE_HL(str) COLOR_HL(str,34)

//测试宏
#define TEST(a, b)\
void a##b();\
__attribute__((constructor)) /*使下一函数优先于main函数被执行*/ \
void add_##a##b() {       \
    add_function(a##b, #a "." #b);/*添加测试组函数到链表*/ \
}\
void a##b()

#define TYPE(a) _Generic((a), /*_Generic c11泛型关键字 将相应类型替换为格式控制字符串*/ \
    int : "%d",\
    float : "f",\
    double : "lf",\
    long long : "lld",\
    char * : "%s",\
    const char * : "%s"\
)

#define P(a, color) {\
    char frm[1000];\
    sprintf(frm, color("%s"), TYPE(a));\
    printf(frm, a);\
}

//断言 根据 cmp 比较 a 和 b 的值
#define EXPECT(a, b, cmp) {\
    __typeof(a) _a = (a);\
    __typeof(b) _b = (b);\
    test_info.total++;\
    if (_a cmp _b) {test_info.success++;}\
    else {\
        printf("\r\n");\
        printf(YELLOW_HL("\t%s:%d: failure\r\n"), __FILE__, __LINE__);/*输出错误点信息*/\
        printf(YELLOW_HL("\t\texpect : " #a " " #cmp " " #b) "\r\n");\
        printf(YELLOW_HL("\t\tactual : "));\
        P(_a,YELLOW_HL);\
        printf(YELLOW_HL(" vs "));\
        P(_b,YELLOW_HL);\
        printf("\r\n\r\n");\
    }\
    printf(GREEN_HL("[-----------] "));\
    printf(#a " " #cmp " " #b " %s\r\n",_a cmp _b ? GREEN("True") : RED("False"));\
}

#define EXPECT_EQ(a, b) EXPECT(a, b, ==)
#define EXPECT_NE(a, b) EXPECT(a, b, !=)
#define EXPECT_LE(a, b) EXPECT(a, b, <=)
#define EXPECT_LT(a, b) EXPECT(a, b, <)
#define EXPECT_GE(a, b) EXPECT(a, b, >=)
#define EXPECT_GT(a, b) EXPECT(a, b, >)

//测试组函数指针类型
typedef void (*FunctestT)();

//测试组函数结构体
typedef struct Function
{
    FunctestT func;
    char *str;
    LinkNode p;
}Function;

//测试信息结构体
typedef struct FunctionInfo
{
    int total, success;
}FunctionInfo;

extern FunctionInfo test_info;

int add(int a, int b);
int RUN_ALL_TESTS();
void add_function(FunctestT, const char *);

#endif // __test_H__
linklist.h
#ifndef __linklist_H__
#define __linklist_H__

#define offset(T, name) (long long)(&(((T *)(NULL))->name))//结构体元素偏移量
#define HEAD(p, T, name) (T *)((char*)(p) - offset(T, name))//移动指针到结构体首地址

typedef struct LinkNode
{
    struct LinkNode *next;
}LinkNode;


#endif // __linklist_H__

test.c
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "../include/test.h"

int func_cnt = 0;//测试组函数个数
Function func_head, *func_tail = &func_head;//头结点 和 尾节点指针
FunctionInfo test_info;//测试节点信息

int RUN_ALL_TESTS() {
    for (LinkNode *p = func_head.p.next; p; p = p->next)
    {
        Function *func = HEAD(p, Function, p);//移动指针到结构体首地址
        test_info.total = 0, test_info.success = 0;
        printf(GREEN_HL("[====RUN====]")RED_HL(" %s\r\n"), func->str);
        func->func();
        free(func->str);
        func->str = NULL;
        free(func);
        func = NULL;
        printf(GREEN_HL("[  "));
        double rate = 1.0 * test_info.success / test_info.total; //测试通过率
        rate *= 100;
        if (fabs(rate - 100) < 1e-6) {
            printf(BLUE_HL("%6.2lf%%"), rate);
        } else {
            printf(RED_HL("%6.2lf%%"), rate);
        }
        printf(GREEN_HL("  ] "));
        printf("total : %d success : %d \r\n", test_info.total,test_info.success);
    }
    return 0;
}

//添加测试组函数到结构体链表
void add_function(FunctestT func,const char *str) {
    Function *temp = calloc(1, sizeof(Function));

    temp->func = func;
    temp->str = strdup(str);
    func_tail->p.next = &(temp->p);
    func_tail = temp;
    func_cnt++;
    return;
}

int add(int a, int b) {
    return a + b;
}
main.c
#include "../include/test.h"

//一组测试例
TEST(test, add0) {
    EXPECT_EQ(add(1, 1), 2);//断言
    EXPECT_NE(add(1, 1), 3);
    EXPECT_LE(add(1, 1), 2);
    EXPECT_LT(add(1, 1), 5);
}

TEST(test, add1) {
    EXPECT_EQ(add(1, 1), 2);
    EXPECT_NE(add(1, 1), 2);
    EXPECT_GE(add(1, 1), 2);
    EXPECT_GT(add(1, 1), 3);
}

int main(int argc, char const *argv[]) {
    //开始测试
    return RUN_ALL_TESTS();
}
Makefile
CC = gcc
.PHONY:clean

test:main.o libtest.a
	$(CC) ./build/main.o -I./include -L./lib -ltest -o ./bin/test

main.o:
	$(CC) -I./include ./src/main.c -c -o ./build/main.o

libtest.a:test.o
	ar -rc ./lib/libtest.a ./build/test.o

test.o:
	$(CC) -I./include ./src/test.c -c -o ./build/test.o

clean:
	rm -f ./build/*.o ./lib/libtest.a

运行结果:

$ make       
gcc -I./include ./src/main.c -c -o ./build/main.o
gcc -I./include ./src/test.c -c -o ./build/test.o
ar -rc ./lib/libtest.a ./build/test.o
gcc ./build/main.o -I./include -L./lib -ltest -o ./bin/test

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值