coding.net地址:https://coding.net/u/Boxer_
ssh:git@git.coding.net:Boxer_/homework.git
https://coding.net/user
本次作业过程还是比较坎坷的,主要是前期准备工作比较复杂,这次工作是了解,熟悉并能使用单元测试框架,主要包括junit,cppunit,cunit。这是上次上课老师留下来的,在课堂上老师介绍了一些这些东西,我努力认真听了一下,然后只记下了名字。由于是用的c++和c编写的代码,我理所应当选用cppunit。
一,cppunit的了解:
当着手这个任务开始,已经是周一下午了,算起来距离deadline已经不远了,倒不是我懒也不是拖延(至少不是主要原因),而是由于其他不可抗事务。一般有这样的规律:距离deadline越近,效率越高。所以从这种意义上来讲,我也是因祸得福,节约了不必要的时间的浪费,毕竟考试前一晚上的复习效率是最高的嘛,而我居然拿出了比一学期考试复习还认真的态度应对一周普通软件工程的作业。还有关于拖延的相关信息大家可以看一下这个视频http://open.163.com/movie/2016/3/Q/E/MBHQSM52F_MBI15O7QE.html
关于cppunit单元测试,我之前不怎么了解 ,选择了百度一下,而结果让我感到不舒服,因为教程基本都是十年前的,推荐的ide还是vc++6.0。这导致了我走了很多弯路,虽然教程是很早之前的,而教程里留下的sourceforge代码托管网站的地址居然隔了十几年还能用,非常靠谱,反观百度云分享链接几个月就应该损失殆尽了。在SourceForge上cppunit的代码一直在不断更新,这让我通过教程给的十年前的地址下载下来了最新的官方项目。但很遗憾的是,无法使用。
可以看到这里面所有的扩展名后面又增加了,v的后缀,不知道什么原因,可能这是服务器版本。导致所有文件无法正常执行了,因为被改了后缀的子文件已经忘记了自己的打开方式,就犹如一个个孤岛,孤立无援。所以这是一个大坑,不知情况者要在这里浪费不少时间。
之后我就一直在网上寻找教程,但是符合自己口味的教程实在太少,前几页的结果也看了很多了,都是零几年写的了,很难理解。就算才开始能跟上作者的步骤,但是作者可能下一步就很跨越,再也理解不能。我厌烦了,我就问老师,老师老师这cppunit是不是已
经淘汰不用了,能不能写个教程。
(部分聊天记录)
之后我明白了,我不会,找不到一定是我还不够努力。既然这是个成熟的软件或者是框架,就一定能用并且也有人用过。然后我想距离现在越短的教程大概越靠谱,百度应该能添加搜所条件。我找了一下果然可以搜最近时间的结果。
我看了下最近一年的文章,搜到了其他人在博客园的教程,比之前我找的教程靠谱233倍。
虽然事后我感觉也是个坑,因为我找他的步骤详细做了,最后几步过不了他提供的github上提供的代码。我看到了他的编译文件,少了几个文件,相当于少了很多类。而我程序报错的代码恰好是因为几个类的定义找不到,当然这是后面才知道的了。然而虽然没按照作者所说的路线走下去,在按照作者步骤做的过程中,我成功的编译了部分文件,得到了部分结果,了解了这个框架的部分构成,更重要的是得到了继续寻找的勇气。
所以个人的经历不是简单的用值不值,简单的得失来衡量的。急于到达目的地,不光跑错了方向,并且也失去了一路的风景。后来我刻意找了一下老版本的cppunit,发现是正常的,之后我用之前教程里得到的经验编译了一下。看了下示例项目,初步了解了下该框架。
鉴于本人学习该框架的痛苦经历,加上有些同学向我对该问题进行交流。我决定说一下流程。算是个简单的教程。
1,你要确保自己有个c++编译器,最好是ide,这里推荐下vs2008。 vs2013或者更高编译出来的东西有问题,我的win10+vs2013环境编译出来的东西无法运行,而实验室的win7+vs2008可以。
2,在http://downloads.sourceforge.net/cppunit/cppunit-1.12.1.tar.gz下载软件,
cppunit版本是cppunit-1.12.1。这个网址打开后过一会会自动下载。
3,解压后是这样的。
4,进入cppunit目录下src文件夹,用VS打开CppUnitLibraries文件,提示需要转换,选择确定。
5,之后迁移项目要花点时间,然后点击生成-批生成-全部生成。之后编译过程比较长,弹出对话框点确定就行了。
6,编译完之后,我们就得到了以下文件,打开testpluginrunner还有图形界面。
7,我们不使用图形界面,退回到一级目录,然后打开example,点击examples.dsw,看到有几个项目。左边的是加载到vs里面的,右边的是文件里的。对比我们可以发现,右边文件夹名字在左边出现的,应该是示例项目。
8,有个项目名字为simple,应该是简单的示例代码,我们点开simple。simple有四个文件,除了我们熟悉的.cpp,.h外还有没见过的makefile文件。
在mian函数的return前面添加system("pause");然后右键项目,生成,然后我们找example/simple/debug下面的simple.exe,看到以下结果。
二,cppunit的进一步了解
再让我们看一下代码
main文件代码
1 #include <cppunit/BriefTestProgressListener.h> 2 #include <cppunit/CompilerOutputter.h> 3 #include <cppunit/extensions/TestFactoryRegistry.h> 4 #include <cppunit/TestResult.h> 5 #include <cppunit/TestResultCollector.h> 6 #include <cppunit/TestRunner.h> 7 8 9 int main( int argc, char* argv[] ) 10 { 11 // Create the event manager and test controller 12 CPPUNIT_NS::TestResult controller; 13 14 // Add a listener that colllects test result 15 CPPUNIT_NS::TestResultCollector result; 16 controller.addListener( &result ); 17 18 // Add a listener that print dots as test run. 19 CPPUNIT_NS::BriefTestProgressListener progress; 20 controller.addListener( &progress ); 21 22 // Add the top suite to the test runner 23 CPPUNIT_NS::TestRunner runner; 24 runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() ); 25 runner.run( controller ); 26 27 // Print test in a compiler compatible format. 28 CPPUNIT_NS::CompilerOutputter outputter( &result, CPPUNIT_NS::stdCOut() ); 29 outputter.write(); 30 system("pause"); 31 return result.wasSuccessful() ? 0 : 1; 32 }
ExampleTestCase.h文件代码
1 #ifndef CPP_UNIT_EXAMPLETESTCASE_H 2 #define CPP_UNIT_EXAMPLETESTCASE_H 3 4 #include <cppunit/extensions/HelperMacros.h> 5 6 /* 7 * A test case that is designed to produce 8 * example errors and failures 9 * 10 */ 11 12 class ExampleTestCase : public CPPUNIT_NS::TestFixture 13 { 14 CPPUNIT_TEST_SUITE( ExampleTestCase ); 15 CPPUNIT_TEST( example ); 16 CPPUNIT_TEST( anotherExample ); 17 CPPUNIT_TEST( testAdd ); 18 CPPUNIT_TEST( testEquals ); 19 CPPUNIT_TEST_SUITE_END(); 20 21 protected: 22 double m_value1; 23 double m_value2; 24 25 public: 26 void setUp(); 27 28 protected: 29 void example(); 30 void anotherExample(); 31 void testAdd(); 32 void testEquals(); 33 }; 34 35 36 #endif
ExampleTestCase.cpp文件代码
1 #include <cppunit/config/SourcePrefix.h> 2 #include "ExampleTestCase.h" 3 4 CPPUNIT_TEST_SUITE_REGISTRATION( ExampleTestCase ); 5 6 void ExampleTestCase::example() 7 { 8 CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, 1.1, 0.05 ); 9 CPPUNIT_ASSERT( 1 == 0 ); 10 CPPUNIT_ASSERT( 1 == 1 ); 11 } 12 13 14 void ExampleTestCase::anotherExample() 15 { 16 CPPUNIT_ASSERT (1 == 2); 17 } 18 19 void ExampleTestCase::setUp() 20 { 21 m_value1 = 2.0; 22 m_value2 = 3.0; 23 } 24 25 void ExampleTestCase::testAdd() 26 { 27 double result = m_value1 + m_value2; 28 CPPUNIT_ASSERT( result == 6.0 ); 29 } 30 31 32 void ExampleTestCase::testEquals() 33 { 34 long* l1 = new long(12); 35 long* l2 = new long(12); 36 37 CPPUNIT_ASSERT_EQUAL( 12, 12 ); 38 CPPUNIT_ASSERT_EQUAL( 12L, 12L ); 39 CPPUNIT_ASSERT_EQUAL( *l1, *l2 ); 40 41 delete l1; 42 delete l2; 43 44 CPPUNIT_ASSERT( 12L == 12L ); 45 CPPUNIT_ASSERT_EQUAL( 12, 13 ); 46 CPPUNIT_ASSERT_DOUBLES_EQUAL( 12.0, 11.99, 0.5 ); 47 }
虽然我对该框架一点不熟悉我们发现了以下代码
1 CPPUNIT_ASSERT( 12L == 12L );//两个长整形12的大小,为真 2 3 CPPUNIT_ASSERT_EQUAL( 12, 13 );//比较12和13是否相等,不相等不通过测试 4 5 CPPUNIT_ASSERT_DOUBLES_EQUAL( 12.0, 11.99, 0.5 )//比较两个double的值是否相差大于0.5.
ExampleTestCase.h文件代码写的是各测试函数的声明。
main函数写的是测试相关代码,发现跟ExampleTestCase中的各函数一点关系都没有,可以安心通过更改部分代码来实现想要进行的但与测试。
三,cppunit的简单实践
本次测试是要测试四则运算程序中关于具体实现运算的两个函数,分别是convert2RPN()和calculateRPN(),二者功能是把算式转化为后缀表达式,并且计算。
以下为测试用例。
test1:1+1 2 test2:55-5 50 test3:33*3 99 test4:50/5 9 test5:1/0 0 test6:21+23*12-6 291 test7:21+23*(12-6) 159 test8:8÷((49+5)÷27) 4 test9:(88/17+31+4) 40.1756
以下为测试代码
ExampleTestCase.h
1 #ifndef CPP_UNIT_EXAMPLETESTCASE_H 2 #define CPP_UNIT_EXAMPLETESTCASE_H 3 #include<string> 4 #include<iostream> 5 #include <cppunit/extensions/HelperMacros.h> 6 using namespace std; 7 class ExampleTestCase : public CPPUNIT_NS::TestFixture 8 { 9 CPPUNIT_TEST_SUITE( ExampleTestCase ); 10 CPPUNIT_TEST( test1 ); 11 CPPUNIT_TEST( test2 ); 12 CPPUNIT_TEST( test3 ); 13 CPPUNIT_TEST( test4 ); 14 CPPUNIT_TEST( test5 ); 15 CPPUNIT_TEST( test6 ); 16 CPPUNIT_TEST( test7 ); 17 CPPUNIT_TEST( test8 ); 18 CPPUNIT_TEST( test9 ); 19 CPPUNIT_TEST_SUITE_END(); 20 public: 21 double res; 22 string s; 23 void test1(); 24 void test2(); 25 void test3(); 26 void test4(); 27 void test5(); 28 void test6(); 29 void test7(); 30 void test8(); 31 void test9(); 32 33 }; 34 35 36 #endif
ExampleTestCase.cpp
之前的代码就不占篇幅了,折叠了先。
1 void convert2RPN(string &s) { 2 stringstream ss; 3 stack<char> stk; 4 for (size_t i = 0; i < s.length(); i++) { 5 if (isdigit(s.at(i))) { 6 ss << s.at(i); 7 if ((i < s.length() - 1 && !isdigit(s.at(i + 1))) 8 || i == s.length() - 1) { 9 ss << ' '; 10 } 11 } 12 else { 13 if (stk.empty()) { 14 stk.push(s.at(i)); 15 } 16 else { 17 switch (s.at(i)) { 18 case '(': 19 stk.push(s.at(i)); 20 break; 21 case ')': 22 while (stk.top() != '(') { 23 ss << stk.top(); 24 stk.pop(); 25 } 26 stk.pop(); 27 break; 28 case '+': 29 case '-': 30 31 while (!stk.empty() && stk.top() != '(') { 32 ss << stk.top(); 33 stk.pop(); 34 } 35 stk.push(s.at(i)); 36 break; 37 case '*': 38 case '/': 39 while (!stk.empty() && (stk.top() == '*' || stk.top() == '/')) { 40 ss << stk.top(); 41 stk.pop(); 42 } 43 stk.push(s.at(i)); 44 break; 45 } 46 } 47 } 48 } 49 while (!stk.empty()) { 50 ss << stk.top(); 51 stk.pop(); 52 } 53 s = ss.str(); 54 }