这篇文章起源于有位朋友在笔者的博客上提问“怎样对于一个Drawing类做单元测试”,因为没有像其它的很多类或者方法,我们可以通过几句Asser.AreEqual(expectedValue, actualValue);来设置断言。这一类的单元测试笔者的经验或者说经历是一片空白,所以很遗憾不能及时帮助那位园友解决问题,而只是按照传统的单元测试的想法提供了点点建议。
一般的单元测试
●有返回值的
一般而言,我们所要应对的单元测试是是来应付类似于public ObjectA SampleFunc(ObjectB param1, ObjectC param2){...},对于这类,应用Xunit系列工具就可以很轻松的摆平了。
首先我们准备一批测试数据,包括输入和输出两块数据,然后一组输入数据inputs[]和一组期望中的输出数据expectedoutput对应,最后用上面提到的Assert就搞定了:
Assert.AreEqual(expectedoutput, new SampleFunc(inputs[0],inputs[1]))
然后泡上一壶茶,运行一下XUnit,绿了也就Ok了。
●改变了某些变量的
这一类的单元测试的对象——改变了某些变量的方法,一般它们没有返回值,但是它们会有一无意的流下了蛛丝马迹,比如改变了某个全局变量的值,或者给某个文件记录插上了一刀子留下了一道记录等等。例如某个方法啥也没干,除了向数据库添加了一条数据,自己还掩耳盗铃返回个void,跟没事的人似的。这类方法,XUnit系列也支持的很好,大不了咱去找受害者查证——查一查数据库看刚才添加的数据,然后来一个Assert一样完事。
今天要说的单元测试
几天要说的单元测试其实在开篇就已经泄露了天机——对Drawing相关的方法进行单元测试,也有人称之为对于GUI单元测试。笔者觉得这个名字怪怪的,因为之前经历的GUI测试大多是在功能自动化测试中才会遇到,但是要让我适应GUI单元测试这种说法,还是有点困难。为了更贴切的来说明这个问题,我取了个名字“Draw a line”单元测试。我们会写一个简单的方法,什么坏事都不干,就在墙壁上涂涂鸦——在WinForm窗口上画一条线,然后我们要对这个(类)方法进行单元测试。听起来很有意思,好像也不难,我开始也是这么想的,但是直到现在也没能找出一个让自己百分百满意的解决方案,不禁开始严重怀疑自己的水准~~
对于这类单元测试怎么做呢?我当时的第一直觉就是——还真不晓得……于是,只好请教Google老师了,或许是自己搜商确实不高,只找到了几篇相关联的,然后其中几篇看不大明白,剩下几篇自认为看明白了的我整理了一下,然后加上一些自己傻瓜式的天真想法。
●检查LOG法
把这种方法首先列出来并不是因为这种方法好,相反是因为我习惯先苦后甜,这么不靠谱的方法拿出来先让大家扔砖头考验自己的砖头承受力。这种方法在于把很难检查的画图过程文本化,然后检查文本来验证我们是怎样画的这个图。
例如:我们有这样一段代码:
protected void DrawALine(PaintEventArgs e) { Graphics g = e.Graphics; myPen.Width = 5; g.DrawLine(myPen, 16, 27, 38, 49); } |
这个方法做的事情是画了一条宽度为5的线,起点是(16,27)而终点是(38,49),对于这个方法,我们可以在末尾加上一段代码:
protected void DrawALine(PaintEventArgs e) ActionLog("Line(5)(16,27,38,49)");//在log中记录下来我们画了一条线,宽度是5,坐标值是(16,27,38,49) |
后面的事情就简单了,在测试方法中运行相应的方法,然后再读取Log看一下Log中是不是做了我们期望做的事。
额,看到这里一定有人在开始骂“不知者无畏”了,其实也是,这种方法不仅傻,而且没有一点点实际用处,也说明不了任何问题。我绞尽脑汁凑了这么个应用场景:
//画一个三角形 protected void DrawATri(PaintEventArgs e) ActionLog("Line(5)(16,27,38,49)");//在log中记录下来我们画了第1条边,宽度是5,坐标值是(16,27,38,49) g.DrawLine(myPen, 38, 49, 54, 49); ActionLog("Line(5)(38, 49, 54, 49)");//在log中记录下来我们画了第2条边,宽度是5,坐标值是(16,27,38,49) g.DrawLine(myPen, 54, 49, 16, 27); ActionLog("Line(5)(54, 49, 16, 27)");//在log中记录下来我们画了第3条边,宽度是5,坐标值是(54, 49, 16, 27) |
这样我们在测试代码中先读出log中的内容,然后逐步检查我们干了什么—画三条边的先后顺序,甚至我们还可以检查这三条边是不是一样宽,这三条边是不是连接到了一起(检查任一边的两端点与另两边中的一个端点相同)。总之,我们是将我们在代码中做的不可见测试的代码用文本的形式表现了出来,然后我们就可以利用XUnit工具来Assert了,绿了,然后完事了。