Step1.我们需要建立项目文件与测试文件的映射关系.
难道要我们去手动创建吗?这可是整个项目啊,里面也许包含了几十个类,数百个方法…当然没那么复杂!实际上,我们需要做的工作很少,只是动动鼠标,等几秒就可以了:)
在VS2005的IDE环境下,选择menu里的Test,继续选New Test项,这时将跳出个窗体,里面可以选择测试项目类型,这里我们选择Unit Test Wizard,确定,输入测试项目名,然后将又出现一个窗体,里面包含你当前的solution里的所有project,我们选上我们的MyCache,确定.OK,看见一个进度条,这是在执行测试代码的映射工作,等结束后,你就会发现,已经建立了一个测试项目了,里面的文件完全对应你的目标项目,每个类包含的方法也是与目标类的方法一一对应,非常简单,cool,mission complete!
Step2.运行我们的测试项目.
接下来,我们怎么进行测试呢?里面有许多的类和方法,很多方法上还带有像TestMethod这样的标签属性,但是我们关心的是,如何进行测试?绝对不是通常的F5来运行:(,在VSTS里,单元测试实际上有专门的管理工具.再次选到menu上的Test选项,移到windows上展开自菜单,里面有好几个选项,我们选择TestManager打开.在IDE窗口内出现了一个视图结构的东西,在分割线的右边是一个listView,里面全是当前测试项目包含的方法,我们随便选几个方法给勾上,右键,Run Checked Test,下边马上有出现了Test Result窗体,里面就是刚才你选择的方法.如果不出意外的话,你的这个窗体内的方法result应该都是failed之类的数据,嗯,先不管这个,最起码,我们已经运行了一次测试项目了,虽然有些奇怪,不过我们已经知道了如何运行一个测试项目了,那么再进入下一个step吧:)
Step3.看看我们的测试代码里都有些什么.
虽然知道了怎么运行测试项目,但莫名其妙的全部出错,是怎么回事呢?我们进入测试项目具体的代码来看看.
我们会发现,每个测试类的名称就是对应的目标类的名称+"test",里面的方法也是如此,如果是构造函数,则是诸如
ConstructorTest或ConstructorTestN这样的形式,N为重载次数.每个方法里面的代码看上去也不奇怪,只是构造参数来调用而已,最后通过断言来判断(用过NUnit的朋友不会陌生吧?).我们试着直接把一个方法里的断言去掉看看,编译,TestManager,run,嘿,果然,去掉断言的方法就pass了!看来蒙老大不难呢,只要把所有的方法的断言都给去掉,然后给老大看测试后的结果,呵呵…
Step4.深入的了解一下方法上带有的属性的含义.
每个方法上几乎都带有TestMethod这个属性,我们直觉告诉我们,这肯定是表示被测试函数的意思.事实也正是如此,在Unit Test里,有许多测试属性,常用的如下:
属性 | 描述 |
TestClass() | 该属性表示一个测试装置。 |
TestMethod() | 该属性表示一个测试用例。 |
AssemblyInitialize() | 在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之前,执行带有该属性的方法。 |
ClassInitialize() | 带有该属性的方法在执行第一个测试之前调用。 |
TestInitialize() | 带有该属性的方法在执行每个 TestMethod() 之前调用。 |
TestCleanup() | 带有该属性的方法在执行每个 TestMethod() 之后调用。 |
ClassCleanup() | 带有该属性的方法在执行 ALL 测试之后调用。 |
AssemblyCleanup() | 在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之后,执行带有该属性的方法。 |
Description() | 提供关于给定 TestMethod() 的描述。 |
Ignore() | 由于某种原因忽略 TestMethod() 或 TestClass()。 |
ExpectedException() | 当测试特定异常时,如果使用该属性指定的异常不是从实现代码引发,则测试不会失败。 |
需要注意的是,上面的属性不是可以适用于所有方法的,比如AssemblyInitialize()和ClassInitialize()是必须是静态方法的属性.
我们可以把初始化的操作放在他们里进行.
Step5.修改测试方法及其断言.
到现在,我们的思路开始清晰起来了,我们要开始做真正的测试了,不是仅仅去掉断言就pass那么简单了:)
我们的测试思路应该是这样:我们调用该方法,需要传入什么值,会影响什么值,当它执行之后,会产生怎样的期待值?我们把期待值与实际的值想比较,同时写下断言失败的message.
还是以我们的MyCahce为例,假如我们有个ListCache类,里面有个AddItemToTop(item)方法,表示把一个item插入到当前链表的头部.我们实际的测试函数该这么写
Guid id = System.Guid.NewGuid();
Item item = new Item(id);
list.AddItemToTop(item);
Assert.AreEqual(id, roomList.FirstLinkedItem.Key, "插入后查询获得的key值与插入的对象的key值不相等!");
通过比对插入后的链表的头部的key与之前保存的key值来判断,这是不是一次成功的插入.
这只是个很简单的例子,我们当然应该根据具体的方法需要实现的功能来定义测试代码.
Step6.OVER
完成了上面5部,相信你已经对VSTS的Unit Test非常的熟悉了,接下来需要做的就是把你需要的测试的method都提供正确的测试代码,注意,这里我们甚至不要考虑我们本身的项目究竟有没有实现该功能,但我们应该该知道,我们需要什么功能.我们只针对应该产生的结果写测试代码.当测试不通过时,我们只需要修改我们的目标项目,而不再需要修改我们的测试项目.这其实正是TDD(测试驱动开发)的思想,我们如果要验证我们的方法有没有错,只需要run一下test即可,真正实现了全自动化单元测试,这里边的实际开发效率的提高,只有你在真正体会过后才能明白:)