C语言单元测试embUnit入门学习

本人原创,转载请注明出处。

目录

学习目标

2.c语言面向对象编程

2.1c语言继承实现

2.1.1子类结构体包含父类结构体

2.1.2子类结构体包含父类结构中的指针

2.1.3结构体和指针有何不同

2.2其他参考

2.2.1宏定义

2.2.2标准库实现StdImpl

3embUnit学习

3.1总体说明

3.2类图

 3.3TestRunner调用过程

3.4TestCaller调用过程

3.5TestResult分析

​​​​​​​3.6TestUI分析


  1. 学习目标

以前一直从事底层驱动和应用开发,这几年中一直从事应用开发,采用C++去实现,最近由于工作原因,需要用C语言进行产品开发,想找一个c语言的单元测试框架,网上找了一个embUnit,这个库不依赖于标准库,方便进行移植,可以在嵌入式和PC上运行。在看代码的过程中,发现这个库的C语言编写,采用了类似C++面向对象的编程,面向对象C++上还算比较熟悉,但C实现还需要补补。由此便有了两个目标。

1)学习该开源库的c语言面向对象编程以及其编程习惯

2)学习embUnit,做为后面工作c语言单元测试框架

2.c语言面向对象编程

下面只是自己对简单的c语言面向对象编程做一个简单的总结记录。有不对之处,请提出讨论。

2.1c语言继承实现

我们知道c语言只能用结构体来实现C++语言类的概念。那怎么样才能继承呢。总结一句话。当一个类的结构体开始格式数据与另一个类结构体格式相同时。那这两个类可以作为父子类。

2.1.1子类结构体包含父类结构体

 例如:

typedef  void(*aFunc)();
      typedef  struct __Father{
       aFunc sFuc;
       char* sName;
}Father,*pFather;

	typedef  struct __ChildS{
        Father sFather;
        char *sFav;
}ChildS,*pChildS;

ChildS结构体开始格式是Father sFather结构体,那ChildS就可以作为Father子类。

测试一下

static void _helloADo()
	{
        printf("helloADo....\r\n");
}

	static const ChildS ChildSA={
    {
       _helloADo,
       "FatherA"
    },
    "ChildA"
};

pFather pf=(pFather)&ChildSA;
pf->sFuc();
printf("name=%s\r\n",pf->sName);

运行结果如下:

helloADo....

name=FatherA

说明ChildS,可以作为Father子类

2.1.2子类结构体包含父类结构中的指针

参考embUnit采用了另一种方法实现。我们看其中用的最多的Test.

 

TestImplement是测试抽象实现函数,有点类似于接口概念,纯虚函数。Test是一个类包含TestImplement指针,这时Test作为父类。子类有两个一个是TestCaller和TestCase.下面抽出部分结构体定义。
struct __Test {
	TestImplement* isa;
};

struct __TestCaller {
    TestImplement* isa; 
    char *name;  
	void(*setUp)(void);
	void(*tearDown)(void);
	int numberOfFixtuers;
	TestFixture	*fixtuers;
};

struct __TestCase {
	TestImplement* isa;
	char *name;
	void(*setUp)(void);
	void(*tearDown)(void);
	void(*runTest)(void);
};

还是记住可以继承的原则。当一个类的结构体开始格式数据与另一个类结构体格式相同时。那这两个类可以作为父子类。这里需要好好理解一下。

所以你看void TestRunner_runTest(Test* test),运行测试都是一个Test对象。这时我们就知道 它可以是TestCaller和TestCase。embUnit顶层测试开始是TestCaller.这些是我们自己由一个个测试用例组成的。

2.1.3结构体和指针有何不同

因为c语言这块面向对象编程才刚开始,c好久不用不太熟悉了。只是谈谈我的个人感觉,这个和c语言特性有关。

指针的好处是方便初始化,可以先把IMP的对象初始化好。再填充。而结构体中结构体初始化填充不是很方便。有兴趣的自己可以测试一下。

2.2其他参考

2.2.1宏定义

embUnit大量用宏定义来生成一个对象。如

#define new_TestFixture(name,test)\

        {\

                 name,\

                 test,\

    }

生成一个TestFixture

其他可以自己研究

2.2.2标准库实现StdImpl

可以参考stdImpl实现了标准库字符串的操作,其中整形转字符串函数,算法比较简洁可以参考下。

3embUnit学习

简单用UML 做了几个类的分析。但毕竟是C语言的,只能大概示意下。

3.1总体说明

三步调用,Start,RunTest,end.

TestRunner_start();

TestRunner_runTest(assertTest_tests());

TestRunner_end();

主要是准备Test对象,

3.2类图

 3.3TestRunner调用过程

 这里不过多解释,根据代码分析一下就可以

​​​​​​​3.4TestCaller调用过程

 TestCaller运行中,生成一个TestCase,然后根据我们应用做好的TestFixture,作为runTest函数,然后在依次启动TestCase Run函数依次运行我们的测试用例

3.5TestResult分析

注意我assertTest,只加了一个testASSERT_EQUAL_STRING,打印结果如下

***********************************************************

**************      Unit Test Start         ***************

***********************************************************

OK (1 tests)

***********************************************************

**************       Unit Test End          ***************

***********************************************************

问题:

 运行assertTest时,明明它有失败的为什么不打印出来呢?

带着问题我们来分析。

  new_TestFixture("testASSERT_EQUAL_STRING",testASSERT_EQUAL_STRING),

是调用testASSERT_EQUAL_STRING该函数

  static void testASSERT_EQUAL_STRING(void)

{

    //接口函数的实现都用TestCaseImplement

    //#define new_TestCase(name,setUp,tearDown,runTest)\

//    {\

//        (TestImplement*)&TestCaseImplement,\

//        name,\

//        setUp,\

//        tearDown,\

//        runTest,\

//    }

        TestCase tcase = new_TestCase("assert_equal_string",NULL,NULL,assert_equal_string_runTest);

        verify(&tcase);

}

它会生成TestCase,就是一个Test对象,再看verify

static void verify(TestCaseRef test)

{

       TestResult result = new_TestResult(NULL);

       test->isa->run(test,&result);

       if (result.failureCount == 0) {

              TEST_FAIL("fail");

       }

}  

生成一个TestResult,注意它没有Listner接口。这时再分析TestCase的Run函数

它的result有POP和PUSH的过程,此处自己体会。由于Listner接口。所以

TEST_ASSERT_EQUAL_STRING("123","456"); 此时用的TestResult没有addfail接口。自然没有失败打印。

  那如何才能有呢。

两种方法:

  1. 直接用TestRunner中的TestResult,
  2. 自己定义TestReult,增加Listener接口。
  1. 很简单,在添加时如下

new_TestFixture("testASSERT_EQUAL_STRING",assert_equal_string_runTest), 直接添加该运行函数。注意结果如下。

***********************************************************

**************      Unit Test Start         ***************

***********************************************************

AssertTest.testASSERT_EQUAL_STRING (..\emb\Tests\assertTest.c 95) exp "123" was "456"

run 1 failures 1

***********************************************************

**************       Unit Test End          ***************

***********************************************************

失败结果打印出来了。

第二种,可以自行添加代码。我加了也可以实现。

​​​​​​​3.6TestUI分析

调用方式如下。

先设置Outputer对象可以是Text和XML,格式。这里又是面向对象的应用。

   TextUIRunner_setOutputter(TextOutputter_outputter());

    TextUIRunner_start();

    TextUIRunner_runTest(assertTest_tests());

    TextUIRunner_runTest(stdImplTest_tests());

    TextUIRunner_runTest(TestCaseTest_tests());

    TextUIRunner_runTest(TestCallerTest_tests());

    TextUIRunner_runTest(TestResultTest_tests());

    TextUIRunner_runTest(RepeatedTestTest_tests());

    TextUIRunner_end();

使用方式差不多 ,不做具体分析,

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值