写单元测试的知识结构(2)——单元测试工具的选用(找个顺手的)
一般的新技术手段的应用分三步(我总结的),问题适配(读书、问人,这时一般处于迷茫状态,尽量避免被煽动被诱惑是很重要的,少讲些主义,多研究问题)、选择工具(一般都有现成的,除了你玩创新(也创新不到哪去)或在Google这种老遇见没人碰到过的问题的地方搞新技术),测试可用性,也就是能不能解决问题(有哥们管这叫趟坑)),经过这三部,基本就可以投入应用了。
这篇基本属于选择工具的一个过程,陈述一些对我选用的测试工具的看法,也包括一些想和做怎么结合的看法,有不同看法欢迎大家拍砖。
如果是固定平台,网上一查差不多推荐文章都一堆,我还看到个标题是“开发者眼中最好的 22 款 GUI 测试工具”(就是这http://www.oschina.net/news/52531/22-gui-testing-tools (真不知他是怎么做的调研(笑))),总之挺乱乎,看了半天各种工具的介绍,最后才逐渐明朗了。总结起来还是先从自身出发,单元测试工具这类东西设计思想差不多,基本都是分三步(3这个数字很有魔力,3生万物啊……),标记测试类、标记测试方法、写断言,然后你的测试工具就拿着你的测试文件当原料去映射运行了。
回归正题,说我怎么选的吧,我的选法就是——都试试(别打我,疼)。先从自己工作考虑,我的IDE是Visual Studio 2013 ,看了看微软自己的MS Test,又用了用,然后找了xUnit系列中的的NUnit用了用(用C#写的嘛,感觉亲切),分别写了单元测试示例(看网上的推广软文一般都是写写" + x / "方法run一下,我也这么干的),上面提到的基础三步的区别也就特性的标记不一样……,然后继续搜索(懒得详尽看两家的文档和实际测试了,毕竟需要一步步深入去体验,不敏捷(笑)),发现还真有人去详尽对比这两种工具的内部功能参数(稍后会验证一下他的说辞,工具的版本会更新嘛……),根据他的观点,在MS Test中有的NUnit都支持,而且做得更全,主要体现在断言的种类多和测试类可以继承达到扩展的特点上,看看他的表格:
原文出处:
先是相同的部分:
MS Test Assert | NUnit Assert | 用途 |
AreEqual |
|
验证值相等 |
AreNotEqual |
|
验证值不相等 |
AreSame |
|
验证引用相等 |
AreNotSame |
|
验证引用不相等 |
Inconclusive |
|
暗示条件还未被验证 |
IsTrue |
|
验证条件为真 |
IsFalse |
|
验证条件为假 |
IsInstanceOfType |
Assert.IsInstanceOf<> |
验证实例匹配类型 |
IsNotInstanceOfType |
Assert.IsNotInstanceOf<> |
验证实例不匹配类型 |
IsNotNull |
|
验证条件为NULL |
IsNull |
|
验证条件不为NULL |
Fail |
|
验证失败 |
然后还有下一段,是NUnit多出来的部分:
- Assert.IsNaN
- Assert.IsEmpty
- Assert.IsNotEmpty
- Assert.Greater
- Assert.GreaterOrEqual
- Assert.Less
- Assert.LessOrEqual
- Assert.IsAssignableFrom
- Assert.IsNotAssignableFrom
- Assert.Igore
- CollectionAssert.IsEmpty
- CollectionAssert.IsNotEmpty
- StringAssert.AreEqualIgnoringCase
- StringAssert.IsMatch
- FileAssert.AreEqual
- FileAssert.AreNotEqual
从断言的命名语义中很容易看出来多出来的断言是干吗的,对于判断这些情况,提供了一些更直接的手段去实现,写得时候少烧一点脑,提高你写测试的效率。
看到NUnit这种好处,很容易投入怀抱了,赤裸裸的诱惑啊,比如StringAssert,人家给出的例子是可以对比配置文件,应用上我们可以减少些耗费眼球的肉眼排错工作(想想上线前,有个家伙不小心把测试环境的配置文件传到了线上环境,跑一下单元测试就把疑点过了,是不是很省力?)。
然后是一个忍住冲动的过程,书不能只读一遍,观点不能只信一个,于是转移注意力又看了看xUNit.Net项目。
这个是叫“周公”的牛人的博客,我发现他专门用NUnit做引子,写了两篇测试工具的对比介绍(很有心嘛),就是下面这篇(这是第二篇,第一篇是NUnit的,由于没有什么独特的,就不推荐重复看了,软文类的看多了一般看一遍就得):
可以看到文章里面他里面对 Jim Newkirk和Brad Wilson 总结出的单元测试工具新特性还是很认同的,主要体现在作者在总结中写的一些话,他认为新工具考虑了更实际的需求,那就是让人们可以更自由地扩展测试工具,而不是局限在工具自身迭代范围内,然后我回到Nuget上一搜,拥趸还真是不少,有自己整前端扩展的也有一些搞测试输出的(我想这也就是为什么xNunit的GUI没有NUnit做的那么全面的原因吧,毕竟不是每个人都对增强用户体验那么感兴趣,带头大哥拉个大旗出个插槽,大家谁写的插件好就插谁的呗,把能力开放出来用分布式的方法去扩展真的是王道)。
包括他自己的GUI就是用xUNit.Extention写的一个扩展(又是赤裸裸的诱惑啊)。
截图中滚动条长长得不见边……
于是还是那个思路,都试试再说,于是我就建项目试了试:
我很懒,所以用了作者原文的测试用例(反正自己写也是一个样子,对于我这样一个测试新手,为了验证学习步骤,也为了看看人家工具怎么配置,反正不是为了写个漂亮的测试((⊙﹏⊙)b)),哈哈,反正是跑成了。
但是,也许是新手的矫情,工具是能用,但是在平台上却不是那么顺利,利用.NET 的测试视图管理器,明明视频里温和的机器男声说的是很方便的囊括这三种(见下面的截图),也就是说测试项目你写了测试代码后,只要编译解决方案,它自动识别出你的单元测试,并可以在管理器里统一管理,但是,我的xUnit在管理器里面就是识别不出来!(而NUnit、MSTest确实都可以用,是我低阶吧(⊙﹏⊙)b),研究后发现,这个坑可能是由于.Net的原因,也就是Framework什么的插件或版本问题。
其实坑还远不如此,和NUnit比起来,从Nuget获取的版本不支持TimeOut特性(真是日了狗了,搜索排前三的包竟然……),但是NUnit则是安装后即开始好使(下面的图中管理器就是跑的NUnit测试项目),然后如果你仔细看文档或上面周公的那篇博客的话,会知道每个测试项目要在配置文件中写nunit的xml节点配置(这个其实也不算太麻烦,但新手你不看不知道啊),还有,NUnit是不支持我找了官网虽然扩展性和自由度以及设计思想前卫的一塌糊涂,但是对于新手村任务来说,用这个还是有一定门槛的,所以,用到这,我再次确定了一下目前的目标,那就是用单元测试带动集成测试的设计,所以工具还是捡个顺手的去推广,先让大家都顺利用上工具(其实也是懒,不过世上也不是上来就有xunit呀,人家从01年就一直陪伴程序员啦,并且很多人也依然在用,比如开源的ZMQ的测试项目,就都是用的NUnit,这项目的老大应该算够颠覆够开放够重量了……)。
顺便说一下,第三张图是微软的说明视频上截取的,他确实用测试管理器把三种框架的测试项目一次性都管理上了……
最后有些对女神恋恋不舍,但是毅然决定还是先用输了NUnit再说(可能这也是周公的博客写这个系列先用NUnit做引子的原因……,因为写好东西的人总是思想非常牛逼,但是做出的东西你得“配得上”她……),等我,屌丝终会逆袭的!(笑)放上周公的表吧,大家感受一下人家写的东西(看着舒服,看着爽),当然,建议你去人家博客看完整版(其实他这数据应该是个翻译版,因为我看到stackoverflow上有个"类似……"咳……)。
以下是引用:
“
下面的表格也是一个关于NUnit、MSTest及xUnit.Net断言的对比。
NUnit 2.2
|
MSTest
|
xUnit.net
|
备注
|
AreEqual
|
AreEqual
|
Equal
|
相等比较
|
AreNotEqual
|
AreNotEqual
|
NotEqual
|
不相等比较
|
AreNotSame
|
AreNotSame
|
NotSame
|
不相同比较
|
AreSame
|
AreSame
|
Same
|
相同比较
|
Contains
|
Contains (on CollectionAssert)
|
Contains
|
|
DoAssert
|
n/a
|
n/a
|
|
n/a
|
DoesNotContain (on CollectionAssert)
|
DoesNotContain
|
|
n/a
|
n/a
|
DoesNotThrow
|
|
Fail
|
Fail
|
n/a
|
可用Assert.True(false, "message")替代
|
Greater
|
n/a
|
n/a
|
可用Assert.True(x > y)替代
|
Ignore
|
Inconclusive
|
n/a
|
|
n/a
|
n/a
|
InRange
|
|
IsAssignableFrom
|
n/a
|
IsAssignableFrom
|
|
IsEmpty
|
n/a
|
Empty
|
|
IsFalse
|
IsFalse
|
False
|
|
IsInstanceOfType
|
IsInstanceOfType
|
IsType
|
|
IsNaN
|
n/a
|
n/a
|
可用Assert.True(double.IsNaN(x))替代
|
IsNotAssignableFrom
|
n/a
|
n/a
|
可用Assert.False(obj is Type)替代
|
IsNotEmpty
|
n/a
|
NotEmpty
|
|
IsNotInstanceOfType
|
IsNotInstanceOfType
|
IsNotType
|
|
IsNotNull
|
IsNotNull
|
NotNull
|
|
IsNull
|
IsNull
|
Null
|
|
IsTrue
|
IsTrue
|
True
|
|
Less
|
n/a
|
n/a
|
可用Assert.True(x < y)替代
|
n/a
|
n/a
|
NotInRange
|
确保数据在某个范围内
|
n/a
|
n/a
|
Throws
|
确保会抛出异常
|
”