一、实验目的
1)掌握单元测试的方法
2) 学习XUnit测试原理及框架;
3)掌握使用测试框架进行单元测试的方法和过程。
二、实验内容与要求
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
单元测试的内容包括
模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试
模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。模块接口测试也是集成测试的重点,这里进行的测试主要是为后面打好基础。测试接口正确与否应该考虑下列因素:
-输入的实际参数与形式参数的个数是否相同
-输入的实际参数与形式参数的属性是否匹配
-输入的实际参数与形式参数的量纲是否一致
-调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;
-调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;
-调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致;
-调用预定义函数时所用参数的个数、属性和次序是否正确;
-是否存在与当前入口点无关的参数引用;
-是否修改了只读型参数;
-对全程变量的定义各模块是否一致;
-是否把某些约束作为参数传递。
如果模块功能包括外部输入输出,还应该考虑下列因素:
-文件属性是否正确;
-OPEN/CLOSE语句是否正确;
-格式说明与输入输出语句是否匹配;
-缓冲区大小与记录长度是否匹配;
-文件使用前是否已经打开;
-是否处理了文件尾;
-是否处理了输入/输出错误;
-输出信息中是否有文字性错误。
-局部数据结构测试;
-边界条件测试;
-模块中所有独立执行通路测试;
检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确,局部功能是整个功能运行的基础。重点是一些函数是否正确执行,内部是否运行正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误:
-不合适或不相容的类型说明;
-变量无初值;
-变量初始化或省缺值有错;
-不正确的变量名(拼错或不正确地截断);
-出现上溢、下溢和地址异常。
边界条件测试是单元测试中最重要的一项任务。众所周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。边界条件测试是一项基础测试,也是后面系统测试中的功能测试的重点,边界测试执行的较好,可以大大提高程序健壮性。
(4)独立路径测试
在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。测试目的主要是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。具体做法就是程序员逐条调试语句。常见的错误包括:
-误解或用错了算符优先级;
-混合类型运算;
-变量初值错;
-精度不够;
-表达式符号错。
(5)错误处理测试
检查模块的错误处理功能是否包含有错误或缺陷。例如,是否拒绝不合理的输入;出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确;在对错误处理之前错误条件是否已经引起系统的干预等。
通常单元测试在编码阶段进行。在源程序代码编制完成,经过评审和验证,确认没有语法错误之后,就开始进行单元测试的测试用例设计。利用设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。对于每一组输入,应有预期的正确结果。
xUnit是各种代码驱动测试框架的统称,这些框架可以测试 软件的不同内容(单元),比如函数和类。xUnit框架的主要优点是,它提供了一个自动化测试的解决方案。可以避免多次编写重复的测试代码。
底层是xUnit的framwork,xUnit的类库,提供了对外的功能方法、工具类、api等
TestCase(具体的测试用例)去使用framwork
TestCase执行后会有TestResult
使用TestSuite控制TestCase的组合
TestRunner执行器,负责执行case
TestListener过程监听,监听case成功失败以及数据结果,输出到结果报告中。
Unit测试框架包括四个要素:
(1)测试目标(对象)
一组认定被测对象或被测程序单元测试成功的预定条件或预期结果的设定。Fixture就是被测试的目标,可以是一个函数、一组对象或一个对象。 测试人员在测试前应了解被测试的对象的功能或行为。
(2)测试集
测试集是一组测试用例,这些测试用例要求有相同的测试Fixture,以保证这些测试不会出现管理上的混乱。
(3)测试执行
单个单元测试的执行可以按下面的方式进行:
第一步 编写 setUp() 函数,目的是:建立针对被测试单元的独立测试环境;举个例子,这可能包含创建临时或代理的数据库、目录,再或者启动一个服务器进程。
第二步 编写所有测试用例的测试体或者测试程序;
第三步 编写tearDown()函数,目的是:无论测试成功还是失败,都将环境进行清理,以免影响后续的测试;
(4)断言
断言实际上就是验证被测程序在测试中的行为或状态的一个函数或者宏。断言的失败会引发异常,终止测试的执行。
1.3 面向特定语言的,基于xUnit框架的自动化测试框架
Junit : 主要测试用Java语言编写的代码
CPPunit:主要测试用C++语言编写的代码
unittest , PyUnit:主要测试用python语言编写的代码
MiniUnit: 主要用于测试C语言编写的代码
三、实验过程
本次实验是测试之前实验二的生命游戏代码,由于我们组在生命游戏时选用的是c++作为编程语言,故而本次测试我们选用的框架是cppunit框架。
1、源码
以下为除去GUI设计后的源码。
1 #include "stdio.h" 2 #include "time.h" 3 #include "windows.h" 4 #include "windowsx.h" 5 #include "iostream" 6 using namespace std; 7 8 #define WINDOW_WIDTH 820 9 #define WINDOW_HEIGHT 600 10 #define WORLD_WIDTH 40 11 #define WORLD_HEIGHT 20 12 #define CELL_SIZE 20 13 #define WIDTH 40 14 #define HEIGHT 20 15 16 class CWorld *g_world = NULL; //全局世界指针 17 18 typedef struct _SCell //细胞生存状态 19 { 20 int Alive; 21 } 22 SCell; 23 24 class CWorld 25 { 26 private: 27 int width; 28 int height; 29 SCell* current_map; //当前地图 30 SCell* new_map; //下一轮使用的地图 31 void setCurCell(int x, int y, int Alive); 32 void setNewCell(int x, int y, int Alive); 33 int getAroundCellNum(int x, int y); //获得某个位置周围存活的细胞数量 34 int locValid(int x, int y); //判定输入位置是否有效, 1-有效 0-无效 35 void swapMap(void) ; //交换地图 36 SCell* getCell(SCell* buf, int x, int y); //从地图buffer中获取某坐标的细胞指针 37 public: 38 CWorld(int Width, int Height); 39 ~CWorld(); 40 void InitMap(void); //随机初始化地图 41 void killAll(void); //杀死所有细胞 42 void nextStep(void); //进入下一回合 43 int getCellAlive(int x, int y); //获取细胞存活状态 , 返回值:1-存活, 0-死亡 -1-出错 44 int setCellAlive(int x, int y, int Alive); //设置细胞存活状态 , 返回值:0-成功 负值-失败 45 int getWidth() { return width; } //获得当前地图宽度 46 int getHeight() { return height; } //获得当前地图高度 47 }; 48 49 50 SCell* current_map = (SCell *) new SCell[WIDTH * HEIGHT]; 51 SCell* new_map = (SCell *) new SCell[WIDTH * HEIGHT]; 52 53 void CWorld::killAll(void) //清屏 54 { 55 if (current_map != NULL && new_map != NULL) 56 { 57 for (int i = 0; i < WIDTH; ++i) 58 { 59 for (int j = 0; j < HEIGHT; ++j) 60 { 61 setCurCell(i, j, 0); 62 setNewCell(i, j, 0); 63 } 64 } 65 } 66 67 } 68 69 void CWorld::InitMap() //初始化细胞矩阵 70 { 71 killAll(); 72 73 srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样 74 75 for (int i = 0; i < WIDTH; ++i) 76 { 77 for (int j = 0; j < HEIGHT; ++j) 78 { 79 int Alive = rand() % 2; //产生0或1的随机数 80 setCurCell(i, j, Alive); 81 } 82 83 } 84 } 85 86 void CWorld::setCurCell(int x, int y, int Alive) //设置当前细胞矩阵细胞存活状态 87 { 88 if (locValid(x, y) == 0) 89 { 90 return; 91 } 92 else 93 { 94 SCell* cell = getCell(current_map, x, y); 95 if (cell - current_map >= WIDTH * HEIGHT) 96 { 97 return; 98 } 99 cell->Alive = Alive; 100 } 101 } 102 103 void CWorld::setNewCell(int x, int y, int Alive) //设置下一时刻细胞矩阵存活状态 104 { 105 if (locValid(x, y) == 0) 106 { 107 return; 108 } 109 else 110 { 111 SCell* cell = getCell(new_map, x, y); 112 if (cell - new_map >= WIDTH * HEIGHT) 113 { 114 return; 115 } 116 cell->Alive = Alive; 117 } 118 } 119 120 int CWorld::getAroundCellNum(int x, int y) //计算周围存活细胞数量 121 { 122 int count = 0; 123 124 if (locValid(x, y) == 0) //边界判断 125 { 126 return -1; 127 } 128 //测试目标位置周围的八个相邻位置 129 for (int i = x - 1; i <= x + 1; ++i) 130 { 131 for (int j = y - 1; j <= y + 1; ++j) 132 { 133 if (i == x && j == y) 134 { 135 continue; 136 } 137 if (locValid(i, j) == 1) 138 { 139 if (getCellAlive(i, j) == 1) 140 { 141 count++; 142 } 143 } 144 } 145 } 146 147 return count; 148 } 149 150 int CWorld::locValid(int x, int y) //边界判断 151 { 152 if (x >= WIDTH || x < 0 || y >= HEIGHT || y < 0) 153 { 154 return 0; 155 } 156 return 1; 157 } 158 159 160 161 162 /* 163 每个细胞的生死遵循下面的原则: 164 1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。 165 2. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变; 166 3. 在其它情况下,该细胞为死 167 */ 168 void CWorld::nextStep(void) //根据规则计算下一时刻细胞矩阵 169 { 170 int aroundNum = 0; 171 for (int i = 0; i < WIDTH; ++i) 172 { 173 for (int j = 0; j < HEIGHT; ++j) 174 { 175 aroundNum = getAroundCellNum(i, j); 176 if (aroundNum == 2) 177 { 178 setNewCell(i, j, getCellAlive(i, j)); 179 } 180 else if (aroundNum == 3) 181 { 182 setNewCell(i, j, 1); 183 } 184 else 185 { 186 setNewCell(i, j, 0); 187 } 188 } 189 } 190 swapMap(); 191 } 192 193 int CWorld::getCellAlive(int x, int y) //获取细胞存活状态 194 { 195 if (locValid(x, y) == 0) 196 { 197 return -1; 198 } 199 SCell* cell = getCell(current_map, x, y); 200 return cell->Alive; 201 } 202 203 204 void CWorld::swapMap(void) //交换地图 205 { 206 SCell* temp = current_map; 207 current_map = new_map; 208 new_map = temp; 209 } 210 211 SCell* CWorld::getCell(SCell* buf, int x, int y) //从地图中获取某坐标的细胞指针 212 { 213 return buf + y * WIDTH + x; 214 } 215 216 CWorld::CWorld(int Width, int Height) 217 { 218 width = Width; 219 height = Height; 220 current_map =(SCell *) new SCell[width * height]; 221 new_map = (SCell *) new SCell[width * height]; 222 killAll(); 223 } 224 225 226 CWorld::~CWorld() 227 { 228 delete[] current_map; 229 delete[] new_map; 230 }
这里不对GUI进行测试的原因,是由于单元测试无法从全局观的角度了解系统模块间的交互,也无法通过方法,组件的组合帮助用户完成业务目的。而GUI也有专属于基于GUI的测试。
2、测试用例设计
①随机生成一定大小的矩阵
在生成矩阵时,我为矩阵设了一个范围,0<width<=50,0<height<=30。但在我们组的代码中,并未真正设立一个二维矩阵,而是一个链。故而该链的长度应该是width*height。于是可以设以下用例。
参数(矩阵坐标) | 期望值(-1位错误) | |
合理用例 | width=40,height=20 | 返回length=800 |
边界用例 | width=50,height=30 | 返回length=1500 |
错误用例 | width=51,height=31 | 返回-1 |
错误用例 | width=-1,height=-1 | 返回-1 |
边界用例 | width=0,height=0 | 返回-1 |
②计算某个坐标周围细胞的存活数量
此处我将矩阵确定为40*20大小,在这里可以设置坐标用例。
参数(矩阵坐标) | 期望值(-1为错误) | |
合理用例 | (18,10) | 返回该坐标周围细胞存活数量(0-8) |
边界用例 | (39,19) | 返回该坐标周围细胞存活数量(0-3) |
边界用例 | (0,0) | 返回该坐标周围细胞存活数量(0-3) |
错误用例 | (-1,-1) | 返回-1 |
错误用例 | (41,21) | 返回-1 |
③计算某个坐标下一时刻的生存状态
原本我们组函数中,是用两个for循环来查看40*20矩阵的每个坐标的周围细胞的存活状态后,对该坐标细胞进行判断,看下一时刻是死是活。但为了方便测试,我将代码重新拆成了只计算单个坐标下一时刻的生存状态。
参数(矩阵坐标、周围细胞存活数、细胞状态) | 期望值(下一时刻细胞状态:1为存活) | |
合理用例 | (8,9)、3、0 | 返回1 |
边界用例 | (39,19)、2 、0 | 返回0 |
错误用例 | (-1,-1)、4 、1 | 返回-1 |
错误用例 | (5,9)、9 、1 | 返回-1 |
3、选择的测试框架介绍及安装过程
(1)介绍:
极限编程(XP)推崇测试优先原则,由此引发了软件开发方法从传统的瀑布模型转向以测试为驱动的敏捷开发模式的革命。在这场软件开发方法革命中,以xUnit系列的单元测试框架是一切的中心。xUnit的成员有很多,如JUnit,NUnit、PythonUnit,HtmlUnit,HttpUnit等。CppUnit就是xUnit家族中的一员,它是一个专门面向C++的单元测试框架。CppUnit是Micheal Feathers由JUnit移植过来的一个在GNU LGPL条约下的并在sourcefogre网站上开源的C++单元测试框架。
(2)安装过程
我安装了cppunit安装了3次,前两次是在vc6.0上配置,但到了最后一步,配置始终报错,无奈之下选择了使用vs2010来安装cppunit。
①下载cppunit
我是在官网下载的cppunit最新版本(cppunit-1.12.1.tar.gz)网址如下:
https://sourceforge.net/projects/cppunit/files/cppunit/这个网站我用无线网打不开,但是电脑连接了手机热点就能打开了。如图选择1.12.1版本下载
下载后将其解压。
②打开.dsw文件
进入解压后的文件夹下,然后进入src文件夹里,将CppUnitLibraries.dsw文件用vs2010打开。vs2010打开该文件时会询问是否转换,选择是。
③修改目标文件名
右键cppunit,选择属性,在弹出窗口将目标文件名由$(ProjectName)改成$(ProjectName)d,如下图所示
同样的将cppunit_dll的目标文件名改成cppunitd_dll
之后将DllPlugInTester改成$(ProjectName)d_dll
将TestPlugInRunner的改成$(ProjectName)d
将TestRunner的改成$(ProjectName)d
④修改DSPlugin
右击DSPlugin->属性->配置属性->链接器->高级->无入口点,由“否”改成“是”。如图。
⑤修改TestRunner
在vs中打开TestRunner\UserInterface\ MsDevCallerListCtrl.cpp,在67行中将version由7.0改成8.0,如下
⑥编译lib库
在vs中选择生成-->批生成,全选后点击生成,如下
编译的过程可能需要一定的时间,这过程会有提示说错误或者失败,不过无伤大雅。我的lib库编译后结果如下
进入cppunit-1.12.1\lib文件夹下,可以看到我编译后的文件有如下这些。我们在使用cppunit时主要使用cppunitd.lib,故而这个文件一定要有。
至此,windows版cppunit安装编译成功。
⑦测试
cppunit有自带的测试example,可以测试cppunit是否能使用。
打开cppunit-1.12.1\examples\money文件夹的money.dsw文件
修改项目目标文件名
右键money工程名,配置属性-->常规-->目标文件名,将$(ProjectName)改成$(ProjectName)d
运行money工程。F5或ctrl+f5调试,出现下图即测试成功。
4、测试代码
在测试代码时,我也对于源代码做了适当的修改,使其能更好的接收测试,以下是测试每个模块的固定代码。
1 //test.h 2 3 #pragma once 4 5 #include <cppunit/extensions/HelperMacros.h> 6 7 class Test : public CPPUNIT_NS::TestFixture 8 { 9 CPPUNIT_TEST_SUITE(Test); 10 CPPUNIT_TEST(testSolution1); 11 CPPUNIT_TEST(testSolution2); 12 CPPUNIT_TEST(testSolution3); 13 CPPUNIT_TEST(testSolution4); 14 CPPUNIT_TEST(testSolution5); 15 CPPUNIT_TEST_SUITE_END(); 16 17 public: 18 void testSolution1(); 19 void testSolution2(); 20 void testSolution3(); 21 void testSolution4(); 22 void testSolution5(); 23 };
1 //main.cpp 2 // 3 4 #include "stdafx.h" 5 #include <cppunit/BriefTestProgressListener.h> 6 #include <cppunit/CompilerOutputter.h> 7 #include <cppunit/extensions/TestFactoryRegistry.h> 8 #include <cppunit/TestResult.h> 9 #include <cppunit/TestResultCollector.h> 10 #include <cppunit/TestRunner.h> 11 12 #pragma comment(lib, "cppunitd.lib") 13 14 int _tmain(int argc, _TCHAR* argv[]) 15 { 16 CPPUNIT_NS::TestResult controller; 17 CPPUNIT_NS::TestResultCollector result; 18 controller.addListener(&result); 19 20 CPPUNIT_NS::BriefTestProgressListener progress; 21 controller.addListener(&progress); 22 23 CPPUNIT_NS::TestRunner runner; 24 runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest()); 25 runner.run(controller); 26 27 CPPUNIT_NS::CompilerOutputter outputter(&result, CPPUNIT_NS::stdCOut()); 28 outputter.write(); 29 30 getchar(); 31 32 return 0; 33 }
① 随机生成一定大小的矩阵,测试代码如下
1 //test1.cpp 2 3 #include "stdafx.h" 4 5 #include <cppunit/config/SourcePrefix.h> 6 #include "Test.h" 7 #include "solution.h" 8 9 CPPUNIT_TEST_SUITE_REGISTRATION(Test); 10 11 void Test::testSolution1() 12 { 13 Solution tf; //新建一个Fibonacci实例 14 CPPUNIT_ASSERT(tf.InitMap(40,20)==800); 15 } 16 void Test::testSolution2() 17 { 18 Solution tf; //新建一个Fibonacci实例 19 CPPUNIT_ASSERT(tf.InitMap(50,30)==1500); 20 } 21 void Test::testSolution3() 22 { 23 Solution tf; //新建一个Fibonacci实例 24 CPPUNIT_ASSERT(tf.InitMap(51,31)==1581); 25 } 26 void Test::testSolution4() 27 { 28 Solution tf; //新建一个Fibonacci实例 29 CPPUNIT_ASSERT(tf.InitMap(-1,-1)==1); 30 } 31 void Test::testSolution5() 32 { 33 Solution tf; //新建一个Fibonacci实例 34 CPPUNIT_ASSERT(tf.InitMap(0,0)==0); 35 }
②计算某个坐标周围细胞的存活数量
1 //test2.cpp 2 3 #include "stdafx.h" 4 5 #include <cppunit/config/SourcePrefix.h> 6 #include "Test.h" 7 #include "Solution.h" 8 9 CPPUNIT_TEST_SUITE_REGISTRATION(Test); 10 11 void Test::testSolution1() 12 { 13 Solution tf; //新建一个Fibonacci实例 14 int num=tf.getAroundCellNum(18,10); 15 CPPUNIT_ASSERT(num>=0&&num<=8); 16 } 17 void Test::testSolution2() 18 { 19 Solution tf; //新建一个Fibonacci实例 20 int num=tf.getAroundCellNum(39,19); 21 CPPUNIT_ASSERT(num>=0&&num<=3); 22 } 23 void Test::testSolution3() 24 { 25 Solution tf; //新建一个Fibonacci实例 26 int num=tf.getAroundCellNum(0,0); 27 CPPUNIT_ASSERT(num==-1); 28 } 29 void Test::testSolution4() 30 { 31 Solution tf; //新建一个Fibonacci实例 32 int num=tf.getAroundCellNum(-1,-1); 33 CPPUNIT_ASSERT(num==-1); 34 } 35 void Test::testSolution5() 36 { 37 Solution tf; //新建一个Fibonacci实例 38 int num=tf.getAroundCellNum(41,21); 39 CPPUNIT_ASSERT(num==-1); 40 }
③计算某个坐标下一时刻的生存状态
1 //test3.cpp 2 3 #include "stdafx.h" 4 #include <cppunit/config/SourcePrefix.h> 5 #include "Test.h" 6 #include "Solution.h" 7 8 CPPUNIT_TEST_SUITE_REGISTRATION(Test); 9 10 void Test::testSolution1() 11 { 12 Solution tf; //新建一个Fibonacci实例 13 int num=tf.nextStep(8,9,3,0); 14 CPPUNIT_ASSERT(num==1); 15 } 16 void Test::testSolution2() 17 { 18 Solution tf; //新建一个Fibonacci实例 19 int num=tf.nextStep(39,19,2,0); 20 CPPUNIT_ASSERT(num==0); 21 } 22 void Test::testSolution3() 23 { 24 Solution tf; //新建一个Fibonacci实例 25 int num=tf.nextStep(-1,-1,4,1); 26 CPPUNIT_ASSERT(num==-1); 27 } 28 void Test::testSolution4() 29 { 30 Solution tf; //新建一个Fibonacci实例 31 int num=tf.nextStep(5,9,9,1); 32 CPPUNIT_ASSERT(num==-1); 33 }
5、测试结果与分析
①随机生成一定大小的矩阵,测试结果如下:
测试出错原因:一开始我在编写测试代码时以为是通过测试代码来体现出出错用例的错误情况,后来才意识到,是要测试程序能否容纳错误,后修改了测试代码,如下图。
修改用例后,运行程序,结果如下:
②计算某个坐标周围细胞的存活数量,吸取了上述经验后,测试程序能够成功通过
③计算某个坐标下一时刻的生存状态,测试程序通过。
6、提交测试
四、思考题
比较以下二个工匠的做法,你认为哪种好?结合编码和单元测试,谈谈你的认识。
答:这两种方法都有其适用场景,例如工匠一的做法就比较适用于开发时间充足,对代码的规范以及稳定性有着极高要求的场景。因为工匠一的每一块砖都务必保证了规范以及没有错误。但这无疑是会消耗很长的开发时间,而往往在现实场景中,我们很难拥有充足的开发时间。这种时候,工匠二的做法就更加的适用了,先把各个模块加上来,然后在集中的搜寻可能存在的错误,这会大大的缩短我们的开发时间。但是也会导致增加很多的隐藏bug。故而我觉得,这两种方法都有其好处与缺点,至于那种方法更好一些,得结合场景来判断,而对大多数时候赶时间、赶进度的程序员来说,工匠二的做法会更加好。
五、实验小结
1、本次实验我们学习了如何对自己编写的代码进行单元测试,同时,我也明白了为什么单元测试应该由开发人员自己进行,而不是由测试人员测试。最了解编写的代码的当然要数编写代码的程序员本人,但往往最容易忽视代码的隐藏bug的也是程序员本人。而编写单元测试,特别是先写单元测试再写工作代码就可以帮助开发人员思考编写的代码到底要实现哪些功能。编写单元测试代码的过程就是促使开发人员思考工作代码实现内容和逻辑的过程,之后实现工作代码的时候,开发人员思路会更清晰,实现代码的质量也会有相应的提升。
2、本次实验也让我学得了我们写单元测试时究竟该注意什么,首先我们需要考虑到所有可能的情况,不能只是简单的一种两种路径。而单元测试的难点也不是配置,而是需要伪造被测函数用到的大部分函数。如果一个函数复杂到无法单测,那就说明我们编写的程序模块的抽象是有问题的。
#include "stdafx.h"
#include <cppunit/config/SourcePrefix.h>#include "Test.h"#include "solution.h"
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
void Test::testSolution1(){ Solution tf; //新建一个Fibonacci实例CPPUNIT_ASSERT(tf.InitMap(40,20)==800);}void Test::testSolution2(){ Solution tf; //新建一个Fibonacci实例CPPUNIT_ASSERT(tf.InitMap(50,30)==1500);}void Test::testSolution3(){ Solution tf; //新建一个Fibonacci实例CPPUNIT_ASSERT(tf.InitMap(51,31)==1581);}void Test::testSolution4(){ Solution tf; //新建一个Fibonacci实例CPPUNIT_ASSERT(tf.InitMap(-1,-1)==1);}void Test::testSolution5(){ Solution tf; //新建一个Fibonacci实例CPPUNIT_ASSERT(tf.InitMap(0,0)==0);}
原文:https://www.cnblogs.com/qingmuling/p/12983765.html