一份用于学习单元测试的案例需求

发表于:2012-1-29 11:03

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:赵劼    来源:51Testing软件测试网采编

  在Main方法中,我们先创建一个MyDriverClient对象,并添加三个查询。如果添加查询时抛出了异常,则关闭MyDriverClient并退出,否则便启动一个额外的线程轮询Receive方法,并将数据打印在屏幕上。Receive方法如果返回null,则表明连接已经断开(其他某处调用了Close方法)。如果Receive方法抛出异常,则将MyDriverClient对象关闭。这段程序将会输出与下面类似的内容:

1, begin
2, begin
3, begin
1, 572224
2, 64186468
3, 9448434
1, 568828
2, 94581343
3, 7291394
...
1, 84165615
2, 26815943
3, 237878844
1, 44716345
Error occurred when receive data.

  Java程序的结构与C#完全相同,区别只是命名方式有所不同。此外C#里的MyDriver项目在Java项目里则变成了myDriver包,自然您也不能修改包里的代码。

  MyClient项目描述

  与MyDriver对应,MyClient则是您要实现的代码(及其单元测试)。其中我已经定义了一些需要对外暴露的类型和成员,您自然也可以添加更多私有或是内部的类型和成员,但一定要保持这些公开的接口不变。

  MyConnection类型是MyClient的核心部分,其中封装了MyDriverClient类的使用。MyConnection的成员如下:

  ● ReconnectInterval:一个静态常量,两次尝试之间的间隔。

  ● MyConnection(string[] uris):构造函数,一个MyConnection保存多个服务器的uri。

  ● void Open():开始连接服务器。Open方法则会在额外的线程连接服务器,其调用本身会立即返回。

  ● IDisposable Subscribe(int queryId, IMySubscriber subscriber):使用queryId订阅数据,subscriber会开始收到与这个queryId相关的所有数据。该方法返回一个IDisposable对象,Dispose后即取消订阅,subscriber不会继续收到数据。queryId为不重复int数据,如果已经存在,则抛出异常。

  ● void Close():断开与服务器的连接(即调用MyDriverClient的Close方法)。该方法“不需要”清除所有订阅,换言之可以再次Open,每个已注册的subscriber会重新开始接受数据。

  ● Connected事件:在每次连接成功时触发。

  ● ConnectFailed事件:在每次连接失败时触发。

  ● Disconnected事件:连接断开时触发。

  与MyDriverClient的“抛出异常”的错误反馈方式不同,MyConnection一旦遇到各种服务器通信的失败时则会选择“重试”。例如:

  ● 调用Open方法之后,会在另一个的线程里从第一个uri开始连接。如果连接失败,则尝试连接下一个uri,如果最后一个uri也失败则重新尝试第一个uri,直至成功。两次尝试之间都需要等待一段时间(ReconnectInterval)。

  ● MyConnection会在另一个线程里轮询MyDriverClient的Receive方法,如果抛出了MyDriverException异常,则关闭当前的MyDriverClient对象,并“立即”重新开始连接下一个uri,如果失败,则再尝试下一个(此时需要有一定间隔),连接成功后重新向各subscriber发送数据。

  IMySubscriber是订阅器的接口,它有两个方法:

  ● void OnBegin():当MyDriverClient的Receive方法收到begin数据时,则调用这个方法。

  ● void OnMessage(string message):当MyDriverClient的Receive方法收到其他数据时,调用这个方法。

  简单地说,MyConnection便是在一个额外的线程中轮询MyDriverClient的Receive方法,并将接收到的MyData对象,根据QueryID分派到对应的subscriber中。接收和分派需要使用一个缓冲区,不能阻塞。值得注意的是,如果调用Subscribe方法的时候尚未连接成功,则相当于只是注册了一个subscriber。假如已经连接了服务器,则在额外的线程里调用AddQuery方法,而Subscribe方法使用要立即返回。同理,取消订阅的Dispose方法也要立即返回,不能被MyDriverClient的RemoveQuery方法阻塞。

  Java项目与C#类似,一些区别则是为了遵循Java的习惯,例如命名以及事件的定义方式,以及使用Closeable接口代替了IDisposable接口。

  实现要求

  代码已经存放在GitHub中,项目Practices01,C#和Java都有。您可以Fork项目,也可以下载到本地。最好您也可以把代码存放到某个在线的Repository中,这样我可以随时查看到您的进展,我也可以立即给您反馈。

  MyClient实现中最重要的部分其实是单元测试,如果您使用测试驱动开发(TDD)亦可,但不做强求,唯一的要求便是有“足够”的单元测试。为了简化问题,您也不需要过于纠缠于一些没有描述到的边界情况,例如还没有调用MyDriverClient的Connect方法便去AddQuery会怎样,或是反复调用Open等等。我们假设所有的使用方式都是“正确”的。但是,对于描述过的情况,例如必须使用“额外的线程”,或是出错之后的重新连接,则必须正确地实现,并包含足够的单元测试。

  在编写单元测试的时候,您可以使用您最喜欢的Mock类库,但请尽量使用普通的Mock类库,而不要使用基于二进制改写或其他方式实现的高级类库,如TypeMock,Moles或是PowerMock。我知道它们可以Mock静态成员或是final成员,的确会有很大帮助,但是我还是希望可以了解如何通过优秀的代码设计来辅助单元测试。像Moq或是EasyMock都是不错的选择。

  如果您有任何反馈,例如上面的需求有任何不清楚的地方,也请立即提出,我会做出改进。这次我打算给三位积极参与并提供较好实现的同学分别送上一本书(或是其他您想要的礼物)作为感谢。

22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号