﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-patsYang-随笔分类-EasyMock</title><link>http://www.blogjava.net/patsYang/category/26368.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 05 Oct 2007 08:51:58 GMT</lastBuildDate><pubDate>Fri, 05 Oct 2007 08:51:58 GMT</pubDate><ttl>60</ttl><item><title>EasyMock简单用</title><link>http://www.blogjava.net/patsYang/archive/2007/10/05/150559.html</link><dc:creator>patsYang</dc:creator><author>patsYang</author><pubDate>Fri, 05 Oct 2007 07:33:00 GMT</pubDate><guid>http://www.blogjava.net/patsYang/archive/2007/10/05/150559.html</guid><wfw:comment>http://www.blogjava.net/patsYang/comments/150559.html</wfw:comment><comments>http://www.blogjava.net/patsYang/archive/2007/10/05/150559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/patsYang/comments/commentRss/150559.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/patsYang/services/trackbacks/150559.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在实际编写<font face="Times New Roman">unit test</font><span style="font-family: 宋体">的过程中，很多情况下我们会需要使用其他的组件（如和数据库相关的测试）。当我们是使用第三方提供的组件时，这并不是太大的问题，因为我们可以假设他们是无错的。但是一旦这些组件是我们自己的组件时，问题就暴露出来了。</span> </p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体"><font size="3">&#8220;单元测试出错了，但到底是谁的错。我的？他的？&#8221;，这种情况显然违反了<span style="color: blue">测试的独立性原则</span>。出现这种情况使得这个测试无法确切的指出是那个单元出现了问题，照成了排错的困难，而且也浪费了时间。同时，过分的将单元的测试代码依赖于其他单元，也照成了其他一些很现实的问题：</font></span></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><span style="font-family: 宋体">在所依赖的单元完成之前，无法顺利的编写单元测试。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><span style="font-family: 宋体">产生环境的依赖性，如运行一个</span><font face="Times New Roman">HttpServletRequest</font><span style="font-family: 宋体">处理器的测试代码必须启动一个</span><font face="Times New Roman">servlet</font><span style="font-family: 宋体">容器。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span style="font-family: 宋体">所有这些问题，都可以使用</span><font face="Times New Roman">Mock Object</font><span style="font-family: 宋体">来解决。使用它的前提是，所依赖单元的接口必须定义清楚。而</span><font face="Times New Roman">EasyMock</font><span style="font-family: 宋体">正是为了这一目的而产生的。</span></font></p>
<p style="margin: 13pt 0cm"><span style="font-family: 黑体"><font size="3">使用目的</font></span></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="font-family: 宋体">通过模拟</span><font face="Times New Roman">unit test</font><span style="font-family: 宋体">所需要的组件，已达到隔离各个</span><font face="Times New Roman">unit test</font><span style="font-family: 宋体">的目的。目前的版本</span><font face="Times New Roman">1.1</font><span style="font-family: 宋体">，它所需要的环境是</span><font face="Times New Roman">jdk1.3.1</font><span style="font-family: 宋体">以上和</span><font face="Times New Roman">junit3.8.1</font><span style="font-family: 宋体">以上。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><font size="3"><span style="font-family: 宋体">可以从</span><a href="http://sourceforge.net/projects/easymock/"><font face="Times New Roman">http://sourceforge.net/projects/easymock/</font></a><span style="font-family: 宋体">处下载。</span></font></p>
<p style="margin: 13pt 0cm"><span style="font-family: 黑体"><font size="3">用法</font></span></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>EasyMock</font><span style="font-family: 宋体">采用&#8220;记录</span><font face="Times New Roman">-----</font><span style="font-family: 宋体">回放&#8221;的工作模式，基本使用步骤：</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">创建</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象的控制对象</span><font face="Times New Roman">Control</font><span style="font-family: 宋体">。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">从控制对象中获取所需要的</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">记录测试方法中所使用到的方法和返回值。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">设置</span><font face="Times New Roman">Control</font><span style="font-family: 宋体">对象到&#8220;回放&#8221;模式。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">进行测试。</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">在测试完毕后，确认</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象已经执行了刚才定义的所有操作。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">使用举例：假设需要测试的对象是</span><font face="Times New Roman">RequestUtil</font><span style="font-family: 宋体">，所需要测试的方法为</span><font face="Times New Roman">getBoolean</font><span style="font-family: 宋体">。此时我们需要模拟一个</span><font face="Times New Roman">HttpServletRequest</font><span style="font-family: 宋体">对象。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">public void testGetStringHttpServletRequestString() {</font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font face="Times New Roman" size="3">public void testGetBoolean() {</font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp; //</font><span style="color: blue; font-family: 宋体">创建</span><span style="color: blue"><font face="Times New Roman">Mock</font></span><span style="color: blue; font-family: 宋体">对象的控制器</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp; MockControl &nbsp;control= MockControl.createControl( HttpServletRequest.class);</font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp; //</font><span style="color: blue; font-family: 宋体">获取</span><span style="color: blue"><font face="Times New Roman">Mock</font></span><span style="color: blue; font-family: 宋体">对象</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp; HttpServletRequest &nbsp;mock= (HttpServletRequest)control.getMock();</font></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 42pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp; //</font><span style="color: blue; font-family: 宋体">设置</span><span style="color: blue"><font face="Times New Roman">getBoolean</font></span><span style="color: blue; font-family: 宋体">中要使用的方法和返回值</span></font></p>
<p style="margin: 0cm 0cm 0pt 17.95pt; text-indent: 42pt"><font face="Times New Roman" size="3">control.expectAndReturn( mock.getParameter( "test"), null);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font size="3"><font face="Times New Roman">//</font><span style="color: blue; font-family: 宋体">设置控制器为</span><span style="color: blue"><font face="Times New Roman">replay</font></span><span style="color: blue; font-family: 宋体">模式</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; control.replay();</font></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</font><span style="color: blue; font-family: 宋体">执行测试</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNull( RequestUtil.getString( mock, "test"));</font></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</font><span style="color: blue; font-family: 宋体">确认</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; control.verify();</font></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman"><font size="3">&nbsp;&nbsp;&nbsp; }</font></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">}</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;通过</span><font face="Times New Roman">EasyMock</font><span style="font-family: 宋体">，执行这段测试代码完全不需要启动一个</span><font face="Times New Roman">servlet</font><span style="font-family: 宋体">容器，在命令行的方式下即可完成，非常的方便。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">3.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">记录需要使用的</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">的行为。在使用一个</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象之前，需要设置我们要用到的方法，以及每个方法的返回值。对于那些没有设置的方法，一旦调用（控制器处于</span><font face="Times New Roman">replay</font><span style="font-family: 宋体">模式），</span><font face="Times New Roman">EasyMock</font><span style="font-family: 宋体">就会抛出异常。记录一个方法，通常可以分成</span><strong><font face="Times New Roman">2</font></strong><strong><span style="font-family: 宋体">步</span></strong><span style="font-family: 宋体">：首先，如同使用正常对象调用这个方法；然后，使用控制器的</span><font face="Times New Roman">setReturnValue</font><span style="font-family: 宋体">函数设置即可。在</span><font face="Times New Roman">1.1</font><span style="font-family: 宋体">中，提供了</span><font face="Times New Roman">expectAndReturn</font><span style="font-family: 宋体">函数，使得</span><font face="Times New Roman">2</font><span style="font-family: 宋体">步可以合而为一。主要的函数大致如下：</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>expectAndReturn</font><span style="font-family: 宋体">，设置期望调用的函数，以及返回值</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>expectAndThrow</font><span style="font-family: 宋体">，设置期望调用的函数，同时期望该次调用抛出异常</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>setReturnValue</font><span style="font-family: 宋体">，设置上一次调用的返回值（如上次调用时，</span><font face="Times New Roman">request.getparameter( &#8220;test&#8221;)</font><span style="font-family: 宋体">，此处设置</span><font face="Times New Roman">request.getparameter( &#8220;test&#8221;)</font><span style="font-family: 宋体">的返回值）</span></font></p>
<p style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt; tab-stops: list 39.0pt"><font size="3"><font face="Times New Roman">-<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>setThrowable</font><span style="font-family: 宋体">，设置上次调用抛出的异常</span></font></p>
<p style="margin: 0cm 0cm 0pt 21pt"><font size="3"><span style="font-family: 宋体">在</span><font face="Times New Roman">EasyMock</font><span style="font-family: 宋体">中还可以设置调用所执行的次数，具体细节请参见对应的</span><font face="Times New Roman">javaDoc</font><span style="font-family: 宋体">。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">4.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">使用举例：</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt"><font face="Times New Roman" size="3">MockControl &nbsp;control= MockControl.createControl( HttpServletRequest.class);</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">HttpServletRequest &nbsp;mock= (HttpServletRequest)control.getMock();</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">mock.getParameter( "test");</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><span style="color: blue"><font face="Times New Roman">//</font></span><span style="color: blue; font-family: 宋体">设置第一次调用</span><span style="color: blue"><font face="Times New Roman">request.getParameter</font></span><span style="color: blue; font-family: 宋体">的返回值</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">control.setReturnValue( null, 1);</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><span style="color: blue"><font face="Times New Roman">//</font></span><span style="color: blue; font-family: 宋体">设置第二次调用</span><span style="color: blue"><font face="Times New Roman">request.getParameter</font></span><span style="color: blue; font-family: 宋体">的返回值</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">control.setReturnValue("this is a test", 1); </font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">control.replay();</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">assertEquals( RequestUtil.getString( mock, "test", "haha"), "haha");</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">assertEquals( RequestUtil.getString( mock, "test"), "this is a test");</font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font face="Times New Roman" size="3">control.verify();</font></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">5.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">在</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象使用结束后，务必调用控制器的</span><font face="Times New Roman">verify</font><span style="font-family: 宋体">函数，以确认</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象的方法得到了调用。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt; tab-stops: list 18.0pt"><font size="3"><font face="Times New Roman">6.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><span style="font-family: 宋体">调用方法的次序，使用</span><font face="Times New Roman">Strict</font><span style="font-family: 宋体">。有时，测试代码依赖于被依赖组件的方法的次序。如在测试与数据库相关代码时，测试代码很有可能是这样的次序：先打开数据库链接，执行操作，关闭链接。为了更好的测试这样的代码，可以使用</span><span style="color: blue"><font face="Times New Roman">MockControl.createStrictControl()</font></span><span style="font-family: 宋体">来创建一个严格的</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">对象控制器，在其中，他会规定</span><font face="Times New Roman">Mock</font><span style="font-family: 宋体">出来的对象的调用次序。</span></font></p>
<p style="margin: 0cm 0cm 0pt 18pt"><font size="3"><span style="font-family: 宋体">以上是</span><font face="Times New Roman">EasyMock</font><span style="font-family: 宋体">的主要使用，至于其他的用法，请参见具体的文档。</span></font></p>
<p style="margin: 13pt 0cm"><span style="font-family: 黑体"><font size="3">PS：</font></span><font size="3"><span style="font-size: 10.5pt; font-family: 宋体">当自己动手实现</span><span style="font-size: 10.5pt; font-family: 'Times New Roman'">Mock</span><span style="font-size: 10.5pt; font-family: 宋体">对象时，不要在</span><span style="font-size: 10.5pt; font-family: 'Times New Roman'">Mock</span><span style="font-size: 10.5pt; font-family: 宋体">对象中实现业务逻辑。</span></font></p>
<img src ="http://www.blogjava.net/patsYang/aggbug/150559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/patsYang/" target="_blank">patsYang</a> 2007-10-05 15:33 <a href="http://www.blogjava.net/patsYang/archive/2007/10/05/150559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>