实现过程
一开始对单元测试这个东西感觉很恐惧,在看过雪晴的博客后,觉得自己可以试试学学,找到了一篇博客,地址:
VS2015安装与C++进行简单单元测试
前面的建立和初始化都比较easy,但很快遇到了一个问题,例子中是对一个类的接口进行的测试,是否能对非类接口(如函数进行测试呢),问了雪晴后,发现应该是可以的,那么就自己试一下吧。
准备试试测试void word_to_word(word_count* word, wprd_count* word1);代码如下:
class word_count { //单词计数器类 public: //friend struct std::hash<word_count>; public: //string* temp;//用来存真实的单词 string* str;//单词的名字 string* num;//数字后缀 int num_rear;//数字后缀的开始位 int str_count;//单词个数 int flag;//判断是否在高频内 int size;//单词 string* word;//单词部分 word_count* next_ptr; word_count(); ~word_count(); }; void word_to_word(word_count* word, word_count* word1) { //把所有的word的值赋给word1 *(word1->str) = *(word->str); *(word1->num) = *(word->num); word1->num_rear=word->num_rear; //数字后缀的开始位 word1->str_count = word->str_count; //单词个数 word1->flag = word->flag; word1->size = word->size; *(word1->word) = *(word->word); word1->next_ptr=word->next_ptr;
但是很快遇到了麻烦,这个函数是为了将对象word中的string和其他值(大多为int类型)都赋给word1类。(在这里仔细想了想发现之所以写这个函数完全是因为对OOP的理解不到家,明明就定义了一个对象,为什么还要做这种复杂的内存转储操作呢……直接定义一个新的不就好了,指针不就是干这个的吗……这就等于在读取单词的过程中我自己疯狂创建了无数个副本,而且他们全部能活到最后,为了验证我的猜测,等一下我要去试一下改改代码,看看是不是这样)。
好吧回到原题,这中间有这样的操作,但博客中只提到了int类型的比较,所以我现在还不确定该怎么使用assert,那么就来摸索一下。
之后我想,string类重载了操作符==,那会不会可以直接对内部的string进行Areequal操作呢,代码如下:
接下来是链接到obj文件上,链接完成,但是程序报错,错误如下:
Microsoft::VisualStudio::TestTools::UnitTesting::Assert::AreEqual”: 15 个重载中没有一个可以转换所有参数类型
查看到代码,发现是areEqual无法判别string类是否相等,继续查阅资料,发现StringAssert类,但是经过阅读发现,StringAssert类大多是对子串和包含/开头结尾来判断的,没有找到合适的方法,于是回头找AreEqual的重载类型,发现,合适的重载参数应该为:
由于本例中都是大写字母,所以就用true即可
但是他继续报错,说明还不对,之后我又发现了string^类型的字串,据说是托管类型的,是什么意思呢 感觉要崩。关于这个托管类型的意义,我感觉有必要专门花时间去学习了了解了。。。不知道看到的老师和同学有没有能教教我的。
最后我放弃了采用Assert,决定#include <assert.h>,然后用assert(word1==word2)的方式来判断。
最终代码如下:
void TestMethod1() { word_count* h1=new word_count(); word_count* h2=new word_count(); h1->flag = 12; *(h1->str) = "strtest123"; *(h1->num) = "123"; *(h1->word) = "strtest"; h1->num_rear = 7; h1->size = 10; h1->str_count = 1; h1->next_ptr = NULL; word_to_word(h1, h2); Assert::AreEqual(h2->flag,h1->flag); Assert::AreEqual(h2->num_rear, h1->num_rear); assert(*(h1->word)== *(h2->word)); assert(*(h1->str)== *(h2->str)); assert(*(h1->num)== *(h2->num)); assert(*(h1->word) == *(h2->word)); Assert::AreEqual(h2->str_count, h1->str_count); Assert::AreEqual(h2->size, h1->size); /* Assert::AreEqual(*(h1->str), *(h2->str)); Assert::AreEqual(*(h1->num), *(h2->num)); */ //Assert::AreEqual(*(h1->word), *(h2->word)); // // TODO: 在此处添加测试逻辑 // };
然后我开始进行链接,具体操作子啊教程中有。但是我没想到的是,由于我的工程是含有预编译头的,所以一开始我只link了../ConsoleApplication4/Debug/word_count.obj,不知道要链接../ ConsoleApplication4/Debug/stdafx.obj,因此一直报如下错误:
先预告一下,事实证明,接下来一个小时更加痛苦的经历都是由于我对warning没有重视,以为问题不大,所以一直崩盘。。。。
这里我们首先要解决error。 关于这个未连接预编译对象,一开始查到的是说要我重新编译原工程,然而这个办法并没有任何卵用。之后我又去问了雪晴,雪晴说她当时是把所有目录下的*.obj都给加入了附加依赖项,所以我也决定用*.obj来代替word_count.h,(之后我也查到了,其实是加入stdafx.h应该就没问题了)。
做完这一步 error没有了,测试程序编译通过但是错误接踵而至,测试程序产生了异常。不过令人欣喜的是,至少测试程序开始运行了,对吧。
接着来处理异常,如下:
上网查看了debug_heap.cpp,发现并不能解决问题,接着又查询了_CrtisValidHeapPointer(block),发现可能是内存泄漏等等的错误,这不禁让我怀疑起自己写的测试程序,于是在测试代码的短短几行里胡乱改动。。。然而在半个小时的徒劳无功下,我最后决定从warning入手。
我首先重新搜索了忽略/EDITANDCONTINUE,但是没有找到端倪。后来在无意中,我看到有篇博客中专门提到需要忽略下面的warning中的两个lib,MSVCRT和UCRTb,然而我在生成测试时,发现IDE似乎在哪里说过了忽略这两个库,这导致我以为这个问题系统已经自动解决(毕竟报的是warning),于是我又浪费了15分钟在怀疑自己和程序上,最后我特意把这两句话上网百度后,发现一篇博客里讲述的一个莫名其妙(因为我不懂)的方法,就是右键TestProject1à属性àC/C++à生成代码à运行库 中改为另一个(MDàMT),见图:
于是我就尝试了一下,但是失败了,于是我把四个选项都尝试了一遍,终于在MDd的状态下,测试继续进行了!而且,总算是显示了测试资源管理器,里面发现测试成功了!下面上一个测试失败的样例和成功的样例(上为失败,下为成功),可以看到失败的原因是因为assert()被设置成了!=,但实际上应该是等于。
至此,这说明我的第一次单元测试的尝试已经完成了!虽然测试的是一个无关痛痒的功能,但是至少完成了从0到1的转变,令人激动!
感想
事实证明,第一次用一样东西最痛苦的就是报错了你都不知道他是为啥,在哪查,连他到底在干什么都不知道,所以从0到1真的需要最大的精力和耐心。
首先,我一直在怀疑测试代码有问题,甚至到最后都在怀疑是不是我的链接过程还是有问题,但是这些怀疑没有来源,所以不可靠。但我却没有关注warning,对warning的查询虽然不是没有。但是就是草草看了一眼就断定不是(应该给自己一巴掌),以后真的一定要有清理warning的习惯啊!
下面附上一些有用的资料:
VS2015 使用微软自带的单元测试框架测试本地C++代码的注意事项
如果测试类使用了预编译头文件的话,要在工程设置中添加stdafx.obj
测试STL容器时,会弹出异常错误对话框.解决办法是忽略MSVCRT.lib 和UCRT.lib
Assert类:
Assert.AreEqual方法:
StringAssert方法:
其他教程:
Visaul Studio2015安装以及c++单元测试使用方法
带你玩转Visual Studio——单元测试
一个关于单元测试未解决的问题: