`
javababy1
  • 浏览: 1220316 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

怎样使用mock object测试一个启动新线程的类

阅读更多

本文是在jmock的网站上发现的,很有实际意义,因为一直用easymock,试了一下jmock,觉得很别扭,方法名以字符串的方式自己输入,容易写错,而且还要继承它自己的基类,不爽。
所以本文的程序样例用easymock重写了。

在下面的例子中,Guard持有一个Alarm的引用,在必要的时候进行报警。

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicinterfaceAlarm{
publicvoidring();
}
<!-- = END of automatically generated HTML code = --><!-- ======================================================== --><!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicclassGuard{
privateAlarmalarm;

publicGuard(Alarmalarm){
this.alarm=alarm;
}

publicvoidgetBored(){
startRingingTheAlarm();
}

privatevoidstartRingingTheAlarm(){
RunnableringAlarmTask=newRunnable(){
publicvoidrun(){
alarm.ring();
}
};
ThreadringAlarmThread=newThread(ringAlarmTask);
ringAlarmThread.start();
}
}

Guard.getBored()的测试代码如下:

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicvoidtestGuardDoesNotRingTheAlarmWhenHeGetsBored(){
Alarmalarm=EasyMock.createMock(Alarm.class);
Guardguard=newGuard(alarm);
guard.getBored();
}

在此例中,预期的异常并没有发生,测试通过了。这是因为alarm抛出的异常是在ringAlarm线程中,而不是在测试主线程中。此问题的根源是试图使用mock object来进行集成测试。用mock object来进行单元测试是希望将测试的单元与系统其他单元相隔离。然而,线程从其特性来说,是属于集成测试的范畴。并发和同步都要涉及到全局范围,线程的创建也用到了操作系统底层的特性。

一种解决方案是将要执行任务的对象与任务的细节相隔离,在它们之间引入一个接口。这样,可以用mock object来测试要执行任务的对象,在集成测试中测试任务的执行。

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicinterfaceTaskRunner{
publicvoidstart(Runnabletask);
}
测试方案如下:
<!-- = END of automatically generated HTML code = --><!-- ======================================================== --><!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicvoidtestGuardDoesNotRingTheAlarmWhenHeGetsBored(){
Alarmalarm=EasyMock.createMock(Alarm.class);
TaskRunnertaskRunner=newImmediateTaskRunner();
Guardguard=newGuard(alarm,taskRunner);
guard.getBored();
}

<!-- = END of automatically generated HTML code = --><!-- ======================================================== --> 在TaskRunner的实现中,如果是启用一个新线程来执行任务,那么又回到了问题的开始,测试还是不能得到希望的异常。我们需要将任务的执行放在TaskRunner相同的线程中。最简单的方法就是立即执行该任务,而不是启线程来执行。在单元测试中,可以直接实现TaskRunner接口,得到如下的任务执行器。

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicclassImmediateTaskRunnerimplementsTaskRunner{
publicvoidstart(Runnabletask){
task.run();
}
}
<!-- = END of automatically generated HTML code = --><!-- ======================================================== -->

Guard代码更新如下:

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicclassGuard{
privateAlarmalarm;

privateTaskRunnertaskRunner;

publicGuard(Alarmalarm,TaskRunnertaskRunner){
this.alarm=alarm;
this.taskRunner=taskRunner;
}

publicvoidgetBored(){
startRingingTheAlarm();
}

privatevoidstartRingingTheAlarm(){
RunnableringAlarmTask=newRunnable(){
publicvoidrun(){
alarm.ring();
}
};
taskRunner.start(ringAlarmTask);
}
}

在实际项目中使用的TaskRunner

<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--><style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicclassConcurrentTaskRunnerimplementsTaskRunner{
publicvoidstart(Runnabletask){
(newThread(task)).start();
}
}
<!-- = END of automatically generated HTML code = --><!-- ======================================================== --> 另一种方案是在Guard.getBored()执行结束后,在测试所在的线程中执行任务。如果Guard中的try/finally 掩盖了任务引起的测试错误,应用此方案则特别适合。
实现的TaskRunner如下:<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--> <style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicclassDelayedTaskRunnerimplementsTaskRunner{
privateList<Runnable>delayedTasks=newArrayList<Runnable>();

publicvoidstart(Runnabletask){
delayedTasks.add(task);
}

publicvoidrunTasks(){
for(Iterator<Runnable>i=delayedTasks.iterator();i.hasNext();){
i.next().run();
i.remove();
}
}
}
<!-- = END of automatically generated HTML code = --><!-- ======================================================== -->对应的测试代码为:<!--CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt--> <style type="text/css"> <!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--> </style> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!-- ======================================================== --><!-- = Java Sourcecode to HTML automatically converted code = --><!-- = Java2Html Converter 5.0 [2006-02-26] by Markus Gebhard markus@jave.de = --><!-- = Further information: http://www.java2html.de = -->
<!-- start source code --> <!-- end source code -->
publicvoidtestGuardDoesNotRingTheAlarmWhenHeGetsBored(){
Alarmalarm=EasyMock.createMock(Alarm.class);
DelayedTaskRunnertaskRunner=newDelayedTaskRunner();
Guardguard=newGuard(alarm,taskRunner);
guard.getBored();
taskRunner.runTasks();
}
<!-- = END of automatically generated HTML code = --><!-- ======================================================== --> 将对象中运行多线程任务的机制提取出来,不仅方便单元测试,而且还能使得程序之间的耦合更松,扩展性更好。比如可以毫不费力的将现在的并发任务处理器替换成线程池。
分享到:
评论

相关推荐

    数据库框架ormlite和单元测试框架junit的使用

    6. **测试环境(Test Environment)**:在Android环境中,可能需要在测试类中使用`@Config`注解来模拟不同的设备环境,例如禁用多线程或模拟特定API级别。 结合ORMLite和JUnit,开发者可以高效地测试数据库操作,...

    UI自动化测试UI自动化测试

    UI自动化测试是软件开发过程中的一个重要环节,它主要用于提高测试效率,减少手动测试的工作量,确保产品的质量和稳定性。UI自动化测试主要关注用户界面(UI)的行为,模拟真实用户与应用程序的交互,验证功能是否按...

    能就业的Python自动化测试开发实战 搞定分布式并发场景(2021新课,完整版).rar

    Python自动化测试是现代软件开发中的重要一环,它能够显著提高测试效率,减少手动测试的工作量,提升软件质量。在“能就业的Python自动化测试开发实战 搞定分布式并发场景”这门课程中,我们将深入探讨如何构建高效...

    Android-YuiHatano——轻量级AndroidDAO单元测试框架

    在实际使用YuiHatano时,首先需要将其添加为项目依赖,然后在测试类中初始化并使用提供的API进行测试操作。框架的API设计简洁明了,易于理解和使用,大大降低了测试的学习成本。 总结一下,Android-YuiHatano是一个...

    Concise UI Tests with Java!.zip

    1. **Selenium WebDriver**:Selenium是一个广泛使用的自动化测试框架,支持多种浏览器和编程语言,包括Java。WebDriver接口允许用Java编写脚本来模拟用户交互,例如点击按钮、输入文本等。 2. **JUnit**:JUnit是...

    互联网高频Java后端面试题20道(适合1~3年)V1.0.31.docx

    2. **可测试性**:在测试时,可以很容易地替换依赖,使用模拟对象(mock object)进行单元测试,提高了测试的效率和质量。 3. **可扩展性**:由于依赖关系由外部控制,因此可以轻松替换或添加新的实现,增强了系统的...

    Struts1与Struts2的12点区别

    为了进行独立测试,通常需要使用如`StrutsTestCase`之类的第三方工具,通过提供一系列的Mock对象(模拟了`HttpServletRequest`和`HttpServletResponse`)来模拟Web容器环境。 - **Struts2**:提供了更好的测试支持。...

    DummyService:侦听端口并对收到的每条消息做出响应的测试服务

    一旦连接建立,它会创建一个新的线程来处理每个客户端的请求,以避免阻塞其他客户端的连接。 3. **多线程** 多线程是Java的一大特性,对于像DummyService这样的服务尤其重要。当多个客户端同时发送请求时,服务...

    Struts1与Struts2本质区别

    - **Struts 2**:每个请求都会创建一个新的Action实例,消除了线程安全问题,同时也减少了开发者在处理并发请求时需要考虑的问题。 #### 3. Servlet依赖方面的对比 - **Struts 1**:Action的方法签名中包含了`...

    自动化测试

    Selenium是一个用于Web应用程序的自动化测试工具,它的WebDriver接口支持多种浏览器,可以实现跨平台的UI自动化测试。使用Java与Selenium结合,我们可以编写可执行各种浏览器操作的测试脚本。 四、Page Object ...

    NHibernate 3.0 经典教程

    **NHibernate** 是一个开放源代码的对象关系映射 (Object-Relational Mapping, ORM) 工具,用于.NET框架下的持久层管理。它允许开发人员将业务对象直接映射到数据库表,并且支持多种.NET语言,如C#等。本书...

    Edoc2自己封装的java api

    为了提高代码的可测试性,我们可以采用依赖注入和模拟对象(Mock Object)策略。例如,创建一个`Edoc2HttpClient`类来负责实际的HTTP请求,然后在`Edoc2Api`类中注入这个实例。在测试时,可以替换为模拟的HTTP客户端...

    java 面试宝典

    4. JUnit:单元测试框架,断言的使用,以及Mockito等mock工具的运用。 以上是Java面试中常见的核心知识点,熟练掌握这些内容能为求职者提供坚实的基础。《张孝祥正在整理Java就业面试题大全.doc》文档可能包含了更...

    Struts2和Struts1的不同

    Struts2提供了一个基础类`ActionSupport`,用于支持常见的功能,如验证、国际化等。此外,Struts2允许使用普通的POJO(Plain Old Java Object)作为Action,这意味着开发者可以更灵活地选择Action的设计方式。 ####...

    stg-pythonWebdriver

    总的来说,"stg-pythonWebdriver" 是关于使用Python和Selenium WebDriver进行Web自动化测试的一个资源,涵盖了从基础的浏览器控制到复杂的测试设计和执行的全面知识。通过深入学习和实践,你可以掌握Web自动化测试的...

    j2ee best practise

    6. **并发处理**:在高并发场景下,合理使用线程池和并发工具类(如Semaphore、CountDownLatch等)能有效避免线程安全问题,提高系统响应速度。 7. **Web服务**:SOAP(Simple Object Access Protocol)和RESTful...

    java初学者必备

    - 一个进程中可以有多个线程共享资源。 12. **不可变对象的重要性** - 不可变对象对于多线程编程非常重要,因为它们的状态不会改变,因此天然具备线程安全性。 13. **Mock对象的作用** - Mock对象用于模拟真实...

    spring 3.2.2官方JAR包

    Spring 3.2.2是该框架的一个稳定版本,包含了丰富的特性和改进。本文将深入探讨Spring 3.2.2的核心概念、主要功能以及它在实际开发中的应用。 1. **依赖注入(Dependency Injection, DI)**: Spring的核心特性之...

Global site tag (gtag.js) - Google Analytics