假设我开发一个C/S型软件,在开发过程中一直用测试来检查代码是否完成我想做的操作了。而且我时不时又会对底层代码进行修正。所以我希望每完成一项小功能,就运行一遍所有测试,以避免出现新功能能用,旧功能却出错的情况出现。
这样做的话,测试用例会非常多,我一般每个函数都会有一个对应的测试用例。
当然,测试用例多不是问题,在一般情况下大部分测试用例的执行时间是很短的,所有测试用例加起来也不会超过1s。
但是有一些用例,执行得特别慢!例如模拟真实环境的测试。因为这些用例可能还要联网,可能还要等待打印机,甚至可能要人为的设置延时等待30s等等。这些用例虽然不多,但是十分耗时。
我遇到的绝大部分测试框架在单线程模式下运行很好。这些框架都是规规矩矩地等待一个用例运行完毕之后,清理产生的垃圾,再运行下一个用例。
但是我希望测试用例能并行运行!因为我会很经常地修改代码并且编译、运行测试,所以我不希望每完成一个新功能、每做一次底层修改就要等上10分钟的测试。
但是有些用例会使用到同一个资源(例如SQL数据库),所以在并行运行测试的时候他们之间会相互影响,导致结果不正确。
所以我想问:是否存在一种现成的能较好地解决以上冲突的测试框架。或者说,我这样设计测试用例其实是错误的,只要遵守某些设计原则,就不会遇到上面的问题了?
举个栗子吧:
我写了2个非常底层的用例,是针对Store模块两个表的功能的测试用例:
大括号里是这个用例调用的函数。
Test.Store.Table1 {
Store.Table1.Insert
Store.Table1.QueryLastInstered
Store.Table1.Update
Store.Table1.Delete
}
Test.Store.Table2 {
Store.Table2.Insert
Store.Table2.QueryLastInstered
Store.Table2.Update
Store.Table2.Delete
}
我用这2个测试用例来检查Server端的Store模块对Table1和Table2的增删查改操作是否正常。为了突出矛盾,我将正常情况下使用的Query改为QueryLastInserted:查询最后一个插入的记录。
我还写了2个测试服务端功能的测试用例:
大括号里是这个用例调用的函数。
Test.Data.Push {
大括号里是这个用例或者函数调用的函数。
Server.Data.Push {
Store.Table1.Insert
Store.Table2.Insert
}
}
Test.Data.Pop {
Server.Data.Pop {
Store.Table1.QueryLastInstered
}
}
这个测试用例检查服务端的Push函数是否正常工作。其中Push这个东西会向Table1和Table2都写入东西,Pop会查询Table1里的最后一个插入的记录。
我还写了1个模拟用户真实操作的测试用例:
Test.Simulate.Message {
Client.SendMessage {
Server.Data.Push {
Store.Table1.Insert
Store.Table2.Insert
}
}
Client.ReceiveMessage {
Server.Data.Pop {
Store.Table1.QueryLastInstered
}
}
}
这2个测试用例用来测试最终功能在真实环境下是否能正常使用。
这个测试用例会模拟鼠标键盘操作,然后打开浏览器,进入我们的网站(位于Internet),发送一条消息,耗时十分长。更有甚者,由于某些要求,项目要求服务器在获取用户请求之后等待1分钟,用户才能收到自己发出去的消息。
这样一来,最后这个用例耗时会十分长。一个这样的测试用例还好,问题是项目中有几个这样的测试用例,就十分头疼了。
考虑代码无问题的情况:
如果按顺序运行测试用例,那不会有任何问题,就是时间有点儿久。
如果异步运行测试用例,那么即使所有的代码都正确,最后一个测试用例仍有可能没法通过,因为乱序执行嘛~
考虑代码有问题的情况:
如果前面的用例失败了,那么后面的用例根本就不用继续了。因为底层都出问题了,还测高层干什么~
所以我想,如果可以指定测试之间的关系(依赖、竞争),那么测试的时间就可以大大缩减。
所以我想问:是否存在一种现成的能较好地解决以上冲突的测试框架。或者说,我这样设计测试用例其实是错误的,只要遵守某些设计原则,就不会遇到上面的问题了?