??xml version="1.0" encoding="utf-8" standalone="yes"?>国产一区日韩,日韩亚洲国产中文字幕欧美,99久久香蕉http://www.aygfsteel.com/shiwenfeng/category/42802.html在不断模ѝ思考、ȝ中一步一步进步!zh-cnSat, 21 Nov 2009 10:20:40 GMTSat, 21 Nov 2009 10:20:40 GMT60EasyMock 使用Ҏ与原理剖?/title><link>http://www.aygfsteel.com/shiwenfeng/archive/2009/11/21/303153.html</link><dc:creator>shiwf</dc:creator><author>shiwf</author><pubDate>Sat, 21 Nov 2009 09:34:00 GMT</pubDate><guid>http://www.aygfsteel.com/shiwenfeng/archive/2009/11/21/303153.html</guid><wfw:comment>http://www.aygfsteel.com/shiwenfeng/comments/303153.html</wfw:comment><comments>http://www.aygfsteel.com/shiwenfeng/archive/2009/11/21/303153.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/shiwenfeng/comments/commentRss/303153.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/shiwenfeng/services/trackbacks/303153.html</trackback:ping><description><![CDATA[<p><span style="color: #993300">本文转蝲:<a><span style="color: #993300">http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/<br /> </span><br /> </a></span>EasyMock 是一套通过单的Ҏ对于指定的接口或cȝ?Mock 对象的类库,它能利用Ҏ口或cȝ模拟来辅助单元测试。本文将?EasyMock 的功能和原理q行介绍Qƈ通过CZ来说明如何?EasyMock q行单元试?br /> <br /> Mock Ҏ是单元测试中常见的一U技术,它的主要作用是模拟一些在应用中不Ҏ构造或者比较复杂的对象Q从而把试与测试边界以外的对象隔离开?/p> <p>~写自定义的 Mock 对象需要额外的~码工作Q同时也可能引入错误。EasyMock 提供了根据指定接口动态构?Mock 对象的方法,避免了手工编?Mock 对象。本文将向您展示如何使用 EasyMock q行单元试Qƈ?EasyMock 的原理进行分析?/p> <p><a name="N10075"><span id="wmqeeuq" class="atitle">1QMock 对象?EasyMock ?/span></a></p> <p><a name="N1007B"><span id="wmqeeuq" class="smalltitle">单元试?Mock Ҏ</span></a></p> <p>单元试是对应用中的某一个模块的功能q行验证。在单元试中,我们帔R到的问题是应用中其它的协同模块尚未开发完成,或者被试模块需要和一些不Ҏ构造、比较复杂的对象q行交互。另外,׃不能肯定其它模块的正性,我们也无法确定测试中发现的问题是由哪个模块引L?/p> <p>Mock 对象能够模拟其它协同模块的行为,被测试模块通过?Mock 对象协作Q可以获得一个孤立的试环境。此外,使用 Mock 对象q可以模拟在应用中不Ҏ构造(?HttpServletRequest 必须?Servlet 容器中才能构造出来)和比较复杂的对象Q如 JDBC 中的 ResultSet 对象Q,从而ɋ试利q行?/p> <p><a name="N10087"><span id="wmqeeuq" class="smalltitle">EasyMock ?/span></a></p> <p>手动的构?Mock 对象会给开发h员带来额外的~码量,而且q些为创?Mock 对象而编写的代码很有可能引入错误。目前,有许多开源项目对动态构?Mock 对象提供了支持,q些目能够Ҏ现有的接口或cd态生成,q样不仅能避免额外的~码工作Q同时也降低了引入错误的可能?/p> <p>EasyMock 是一套用于通过单的Ҏ对于l定的接口生?Mock 对象的类库。它提供Ҏ口的模拟Q能够通过录制、回放、检查三步来完成大体的测试过E,可以验证Ҏ的调用种cR次数、顺序,可以?Mock 对象q回指定的值或抛出指定异常。通过 EasyMockQ我们可以方便的构?Mock 对象从而单元试利q行?/p> <p><a name="N10093"><span id="wmqeeuq" class="smalltitle">安装 EasyMock</span></a></p> <p>EasyMock 是采?MIT license 的一个开源项目,您可以在 Sourceforge 上下载到相关?zip 文g。目前您可以下蝲?EasyMock 最新版本是2.3Q它需要运行在 Java 5.0 q_上。如果您的应用运行在 Java 1.3 ?1.4 q_上,您可以选择 EasyMock1.2。在解压~?zip 包后Q您可以扑ֈ easymock.jar q个文g。如果您使用 Eclipse 作ؓ IDEQ把 easymock.jar d到项目的 Libraries 里就可以使用了(如下图所C)。此外,׃我们的测试用例运行在 JUnit 环境中,因此您还需?JUnit.jarQ版?.8.1以上Q?/p> <br /> <a name="classpath.gif"><strong>?QEclipse 目中的 Libraries</strong></a><br /> <img height="493" alt="Eclipse 目中的 Libraries" src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/classpath.gif" width="572" /> <br /> <br /> <p><a name="N100AB"><span id="wmqeeuq" class="atitle">2Q?EasyMock q行单元试</span></a></p> <p>通过 EasyMockQ我们可以ؓ指定的接口动态的创徏 Mock 对象Qƈ利用 Mock 对象来模拟协同模块或是领域对象,从而单元试利q行。这个过E大致可以划分ؓ以下几个步骤Q?/p> <ul> <li>使用 EasyMock 生成 Mock 对象Q? <li>讑֮ Mock 对象的预期行为和输出Q? <li>?Mock 对象切换?Replay 状态; <li>调用 Mock 对象Ҏq行单元试Q? <li>?Mock 对象的行行验证?</li> </ul> <p>接下来,我们对以上的几个步骤逐一q行说明。除了以上的基本步骤外,EasyMock q对Ҏ?Mock 对象cd、特定的参数匚w方式{功能提供了支持Q我们将在之后的章节中进行说明?/p> <p><a name="N100C9"><span id="wmqeeuq" class="smalltitle">使用 EasyMock 生成 Mock 对象</span></a></p> <p>Ҏ指定的接口或c,EasyMock 能够动态的创徏 Mock 对象QEasyMock 默认只支持ؓ接口生成 Mock 对象Q如果需要ؓcȝ?Mock 对象Q在 EasyMock 的主上有扩展包可以实现此功能)Q我们以 <code>ResultSet</code> 接口Z说明EasyMock的功能?code>java.sql.ResultSet</code> 是每一?Java 开发h员都非常熟悉的接口:</p> <br /> <a name="code001"><strong>清单1QResultSet 接口</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="42" sizcache="2"> <tbody sizset="42" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public interface java.sql.ResultSet { ...... public abstract java.lang.String getString(int arg0) throws java.sql.SQLException; public abstract double getDouble(int arg0) throws java.sql.SQLException; ...... } </pre> </td> </tr> </tbody> </table> <br /> <p>通常Q构Z个真实的 <code>RecordSet</code> 对象需要经q一个复杂的q程Q在开发过E中Q开发h员通常会编写一?<code>DBUtility</code> cL获取数据库连?<code>Connection</code>Qƈ利用 <code>Connection</code> 创徏一?<code>Statement</code>。执行一?<code>Statement</code> 可以获取C个或多个 <code>ResultSet</code> 对象。这L构造过E复杂ƈ且依赖于数据库的正确q行。数据库或是数据库交互模块出现问题,都会影响单元试的结果?/p> <p sizset="43" sizcache="2">我们可以使用 EasyMock 动态构?<code>ResultSet</code> 接口?Mock 对象来解册个问题。一些简单的试用例只需要一?Mock 对象Q这Ӟ我们可以用以下的Ҏ来创?Mock 对象Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="43" sizcache="2"> <tbody sizset="43" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">ResultSet mockResultSet = createMock(ResultSet.class); </pre> </td> </tr> </tbody> </table> <br /> </p> <p>其中 <code>createMock</code> ?<code>org.easymock.EasyMock</code> cL提供的静态方法,你可以通过 static import 其引入Q注Qstatic import ?java 5.0 所提供的新Ҏ)?/p> <p sizset="44" sizcache="2">如果需要在相对复杂的测试用例中使用多个 Mock 对象QEasyMock 提供了另外一U生成和理 Mock 对象的机Ӟ <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="44" sizcache="2"> <tbody sizset="44" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">IMocksControl control = EasyMock.createControl(); java.sql.Connection mockConnection = control.createMock(Connection.class); java.sql.Statement mockStatement = control.createMock(Statement.class); java.sql.ResultSet mockResultSet = control.createMock(ResultSet.class); </pre> </td> </tr> </tbody> </table> <br /> </p> <p><code>EasyMock</code> cȝ <code>createControl</code> Ҏ能创Z个接?<code>IMocksControl</code> 的对象,该对象能创徏q管理多?Mock 对象。如果需要在试中用多?Mock 对象Q我们推荐您使用q一机制Q因为它在多?Mock 对象的管理上提供了相对便LҎ?/p> <p>如果您要模拟的是一个具体类而非接口Q那么您需要下载扩展包 EasyMock Class Extension 2.2.2。在对具体类q行模拟Ӟ您只要用 <code>org.easymock.classextension.EasyMock</code> cM的静态方法代?<code>org.easymock.EasyMock</code> cM的静态方法即可?/p> <p><a name="N1013C"><span id="wmqeeuq" class="smalltitle">讑֮ Mock 对象的预期行为和输出</span></a></p> <p>在一个完整的试q程中,一?Mock 对象会l历两个状态:Record 状态和 Replay 状态。Mock 对象一l创建,它的状态就被置?Record。在 Record 状态,用户可以讑֮ Mock 对象的预期行为和输出Q这些对象行录制下来Q保存在 Mock 对象中?/p> <p>d Mock 对象行ؓ的过E通常可以分ؓ以下3步: <ul> <li>?Mock 对象的特定方法作用; <li>通过 <code>org.easymock.EasyMock</code> 提供的静态方?<code>expectLastCall</code> 获取上一ơ方法调用所对应?IExpectationSetters 实例Q? <li>通过 <code>IExpectationSetters</code> 实例讑֮ Mock 对象的预期输出?</li> </ul> <p> </p> <p><strong>讑֮预期q回?/strong> </p> <p sizset="45" sizcache="2">Mock 对象的行为可以简单的理解?Mock 对象Ҏ的调用和Ҏ调用所产生的输出。在 EasyMock 2.3 中,?Mock 对象行ؓ的添加和讄是通过接口 <code>IExpectationSetters</code> 来实现的。Mock 对象Ҏ的调用可能生两U类型的输出Q(1Q生返回|Q?Q抛出异常。接?<code>IExpectationSetters</code> 提供了多U设定预期输出的ҎQ其中和讑֮q回值相对应的是 andReturn ҎQ? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="45" sizcache="2"> <tbody sizset="45" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">IExpectationSetters<T> andReturn(T value);</pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="46" sizcache="2">我们仍然?<code>ResultSet</code> 接口?Mock 对象ZQ如果希望方?<code>mockResult.getString(1)</code> 的返回gؓ "My return value"Q那么你可以使用以下的语句: <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="46" sizcache="2"> <tbody sizset="46" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockResultSet.getString(1); expectLastCall().andReturn("My return value"); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="47" sizcache="2">以上的语句表C?<code>mockResultSet</code> ?<code>getString</code> Ҏ被调用一ơ,q次调用的返回值是 "My return value"。有Ӟ我们希望某个Ҏ的调用Lq回一个相同的|Z避免每次调用都ؓ Mock 对象的行行一ơ设定,我们可以用设|默认返回值的ҎQ? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="47" sizcache="2"> <tbody sizset="47" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">void andStubReturn(Object value); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="48" sizcache="2">假设我们创徏?<code>Statement</code> ?<code>ResultSet</code> 接口?Mock 对象 mockStatement ?mockResultSetQ在试q程中,我们希望 mockStatement 对象?<code>executeQuery</code> ҎLq回 mockResultSetQ我们可以用如下的语句 <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="48" sizcache="2"> <tbody sizset="48" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockStatement.executeQuery("SELECT * FROM sales_order_table"); expectLastCall().andStubReturn(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> </p> <p>EasyMock 在对参数D行匹配时Q默认采?<code>Object.equals()</code> Ҏ。因此,如果我们?<code>"select * from sales_order_table"</code> 作ؓ参数Q预期方法将不会被调用。如果您希望上例中的 SQL 语句能不区分大小写,可以用特D的参数匚w器来解决q个问题Q我们将?"?EasyMock 中用参数匹配器" 一章对此进行说明?/p> <p><strong>讑֮预期异常抛出</strong> </p> <p sizset="49" sizcache="2">对象行ؓ的预期输出除了可能是q回值外Q还有可能是抛出异常?code>IExpectationSetters</code> 提供了设定预期抛出异常的ҎQ? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="49" sizcache="2"> <tbody sizset="49" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">IExpectationSetters<T> andThrow(Throwable throwable); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="50" sizcache="2">和设定默认返回值类|<code>IExpectationSetters</code> 接口也提供了讑֮抛出默认异常的函敎ͼ <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="50" sizcache="2"> <tbody sizset="50" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">void andStubThrow(Throwable throwable); </pre> </td> </tr> </tbody> </table> <br /> </p> <p><strong>讑֮预期Ҏ调用ơ数</strong> </p> <p sizset="51" sizcache="2">通过以上的函敎ͼ您可以对 Mock 对象特定行ؓ的预期输行设定。除了对预期输出q行讑֮Q?code>IExpectationSetters</code> 接口q允许用户对Ҏ的调用次C出限制。在 <code>IExpectationSetters</code> 所提供的这一cL法中Q常用的一U是 <code>times</code> ҎQ? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="51" sizcache="2"> <tbody sizset="51" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">IExpectationSetters<T>times(int count); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="52" sizcache="2">该方法可?Mock 对象Ҏ的调用次数进行确切的讑֮。假设我们希?mockResultSet ?<code>getString</code> Ҏ在测试过E中被调?ơ,期间的返回值都?"My return value"Q我们可以用如下语句Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="52" sizcache="2"> <tbody sizset="52" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockResultSet.getString(1); expectLastCall().andReturn("My return value").times(3); </pre> </td> </tr> </tbody> </table> <br /> <br /> 注意?<code>andReturn</code> ?<code>andThrow</code> Ҏ的返回g然是一?<code>IExpectationSetters</code> 实例Q因此我们可以在此基上l调?<code>times</code> Ҏ?/p> <p>除了讑֮定的调用次敎ͼ<code>IExpectationSetters</code> q提供了另外几种讑֮非准调用次数的ҎQ?br /> <code>times(int minTimes, int maxTimes)</code>Q该Ҏ最被调用 minTimes ơ,最多被调用 maxTimes ơ?br /> <code>atLeastOnce()</code>Q该Ҏ臛_被调用一ơ?br /> <code>anyTimes()</code>Q该Ҏ可以被调用Q意次?</p> <p sizset="53" sizcache="2">某些Ҏ的返回值类型是 voidQ对于这一cL法,我们无需讑֮q回|只要讄调用ơ数可以了。以 <code>ResultSet</code> 接口?<code>close</code> ҎZQ假讑֜试q程中,该方法被调用3?ơ: <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="53" sizcache="2"> <tbody sizset="53" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockResultSet.close(); expectLastCall().times(3, 5); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="54" sizcache="2">Z化书写,EasyMock q提供了另一U设?Mock 对象行ؓ的语句模式。对于上例,您还可以它写成Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="54" sizcache="2"> <tbody sizset="54" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">expect(mockResult.close()).times(3, 5); </pre> </td> </tr> </tbody> </table> <br /> <br /> q个语句和上例中的语句功能是完全相同的?</p> <p><a name="chap2.3"><span id="wmqeeuq" class="smalltitle">?Mock 对象切换?Replay 状?/span></a></p> <p>在生?Mock 对象和设?Mock 对象行ؓ两个阶段QMock 对象的状态都?Record 。在q个阶段QMock 对象会记录用户对预期行ؓ和输出的讑֮?/p> <p sizset="55" sizcache="2">在?Mock 对象q行实际的测试前Q我们需要将 Mock 对象的状态切换ؓ Replay。在 Replay 状态,Mock 对象能够Ҏ讑֮对特定的Ҏ调用作出预期的响应。将 Mock 对象切换?Replay 状态有两种方式Q您需要根?Mock 对象的生成方式进行选择。如?Mock 对象是通过 <code>org.easymock.EasyMock</code> cL供的静态方?createMock 生成的(W?节中介绍的第一U?Mock 对象生成ҎQ,那么 <code>EasyMock</code> cL供了相应?replay Ҏ用于?Mock 对象切换?Replay 状态: <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="55" sizcache="2"> <tbody sizset="55" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">replay(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="56" sizcache="2">如果 Mock 对象是通过 <code>IMocksControl</code> 接口提供?<code>createMock</code> Ҏ生成的(W?节中介绍的第二种Mock对象生成ҎQ,那么您依旧可以通过 <code>IMocksControl</code> 接口对它所创徏的所?Mock 对象q行切换Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="56" sizcache="2"> <tbody sizset="56" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">control.replay(); </pre> </td> </tr> </tbody> </table> <br /> </p> <p>以上的语句能在W?节中生成?mockConnection、mockStatement ?mockResultSet {??Mock 对象都切换成 Replay 状态?/p> <p><a name="chap2.4"><span id="wmqeeuq" class="smalltitle">调用 Mock 对象Ҏq行单元试</span></a></p> <p>Z更好的说?EasyMock 的功能,我们引入 src.zip 中的CZ来解?Mock 对象在实际测试阶D늚作用。其中所有的CZ代码都可以在 src.zip 中找到。如果您使用?IDE ?EclipseQ在导入 src.zip 之后您可以看?Workspace 中增加的 projectQ如下图所C)?/p> <br /> <a name="classpath.gif"><strong>?Q导?src.zip 后的 Workspace</strong></a><br /> <img height="216" alt="导入src.zip后的Workspace" src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/workspace.gif" width="281" /> <br /> <p>下面是示例代码中的一个接?<code>SalesOrder</code>Q它的实现类 <code>SalesOrderImpl</code> 的主要功能是从数据库中读取一?Sales Order ?Region ?Total PriceQƈҎd的数据计该 Sales Order ?Price LevelQ完整的实现代码都可以在 src.zip 中找刎ͼQ?/p> <br /> <a name="code001"><strong>清单2QSalesOrder 接口</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="57" sizcache="2"> <tbody sizset="57" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public interface SalesOrder { …… public void loadDataFromDB(ResultSet resultSet) throws SQLException; public String getPriceLevel(); } </pre> </td> </tr> </tbody> </table> <br /> <p>其实现类 <code>SalesOrderImpl</code> 中对 <code>loadDataFromDB</code> 的实现如下:</p> <br /> <a name="code001"><strong>清单3QSalesOrderImpl 实现</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="58" sizcache="2"> <tbody sizset="58" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public class SalesOrderImpl implements SalesOrder { ...... public void loadDataFromDB(ResultSet resultSet) throws SQLException { orderNumber = resultSet.getString(1); region = resultSet.getString(2); totalPrice = resultSet.getDouble(3); } ...... } </pre> </td> </tr> </tbody> </table> <br /> <p>Ҏ <code>loadDataFromDB</code> d?<code>ResultSet</code> 对象包含的数据。当我们之前定义的 Mock 对象调整?Replay 状态,q将该对象作为参C入,那么 Mock 对象的方法将会返回预先定义的预期q回倹{完整的 TestCase 如下Q?/p> <br /> <a name="code001"><strong>清单4Q完整的TestCase</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="59" sizcache="2"> <tbody sizset="59" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public class SalesOrderTestCase extends TestCase { public void testSalesOrder() { IMocksControl control = EasyMock.createControl(); ...... ResultSet mockResultSet = control.createMock(ResultSet.class); try { ...... mockResultSet.next(); expectLastCall().andReturn(true).times(3); expectLastCall().andReturn(false).times(1); mockResultSet.getString(1); expectLastCall().andReturn("DEMO_ORDER_001").times(1); expectLastCall().andReturn("DEMO_ORDER_002").times(1); expectLastCall().andReturn("DEMO_ORDER_003").times(1); mockResultSet.getString(2); expectLastCall().andReturn("Asia Pacific").times(1); expectLastCall().andReturn("Europe").times(1); expectLastCall().andReturn("America").times(1); mockResultSet.getDouble(3); expectLastCall().andReturn(350.0).times(1); expectLastCall().andReturn(1350.0).times(1); expectLastCall().andReturn(5350.0).times(1); control.replay(); ...... int i = 0; String[] priceLevels = { "Level_A", "Level_C", "Level_E" }; while (mockResultSet.next()) { SalesOrder order = new SalesOrderImpl(); order.loadDataFromDB(mockResultSet); assertEquals(order.getPriceLevel(), priceLevels[i]); i++; } control.verify(); } catch (Exception e) { e.printStackTrace(); } } } </pre> </td> </tr> </tbody> </table> <br /> <p>在这个示例中Q我们首先创Z <code>ResultSet</code> ?Mock 对象 moResultSetQƈ记录?Mock 对象的预期行为。之后我们调用了 <code>control.replay()</code>Q将 Mock 对象的状态置?Replay 状态。在实际的测试阶D,Sales Order 对象?<code>loadDataFromDB</code> Ҏ调用?mockResultSet 对象?<code>getString</code> ?<code>getDouble</code> Ҏd mockResultSet 中的数据。Sales Order 对象Ҏd的数据计出 Price LevelQƈ和预期输行比较?</p> <p><a name="chap2.5"><span id="wmqeeuq" class="smalltitle">?Mock 对象的行行验?/span></a></p> <p>在利?Mock 对象q行实际的测试过E之后,我们q有一件事情没有做Q对 Mock 对象的方法调用的ơ数q行验证?/p> <p sizset="60" sizcache="2">Z验证指定的方法调用真的完成了Q我们需要调?<code>verify</code> Ҏq行验证。和 <code>replay</code> ҎcMQ您需要根?Mock 对象的生成方式来选用不同的验证方式。如?Mock 对象是由 <code>org.easymock.EasyMock</code> cL供的 <code>createMock</code> 静态方法生成的Q那么我们同样采?<code>EasyMock</code> cȝ静态方?<code>verify</code> q行验证Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="60" sizcache="2"> <tbody sizset="60" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">verify(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="61" sizcache="2">如果Mock对象是有 <code>IMocksControl</code> 接口所提供?<code>createMock</code> Ҏ生成的,那么采用该接口提供的 <code>verify</code> ҎQ例如第1节中?<code>IMocksControl</code> 实例 controlQ? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="61" sizcache="2"> <tbody sizset="61" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">control.verify(); </pre> </td> </tr> </tbody> </table> <br /> </p> <p>对 control 实例所生成?Mock 对象 mockConnection、mockStatement ?mockResultSet {进行验证。如果将上例?<code>expectLastCall().andReturn(false).times(1)</code> 的预期次C改ؓ2Q在 Eclipse 中将可以看到Q?/p> <br /> <a name="asserterror.gif"><strong>?QMock对象验证p|</strong></a><br /> <img height="286" alt="Mock对象验证p|" src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/asserterror.gif" width="557" /> <br /> <p><a name="chap2.6"><span id="wmqeeuq" class="smalltitle">Mock 对象的重?/span></a></p> <p>Z避免生成q多?Mock 对象QEasyMock 允许对原?Mock 对象q行重用。要?Mock 对象重新初始化,我们可以采用 reset Ҏ。和 replay ?verify ҎcMQEasyMock 提供了两U?reset 方式Q(1Q如?Mock 对象是由 <code>org.easymock.EasyMock</code> cM的静态方?<code>createMock</code> 生成的,那么?Mock 对象的可以用 <code>EasyMock</code> cȝ静态方?<code>reset</code> 重新初始化;Q?Q如?Mock Ҏ是由 <code>IMocksControl</code> 实例?<code>createMock</code> Ҏ生成的,那么?<code>IMocksControl</code> 实例Ҏ <code>reset</code> 的调用将会把所有该实例创徏?Mock 对象重新初始化?/p> <p>在重新初始化之后QMock 对象的状态将被置?Record 状态?/p> <p><a name="chap3"><span id="wmqeeuq" class="atitle"><br /> <br /> <br /> 3Q在 EasyMock 中用参数匹配器</span></a></p> <p><a name="chap3.1"><span id="wmqeeuq" class="smalltitle">EasyMock 预定义的参数匚w?/span></a></p> <p sizset="65" sizcache="2">在?Mock 对象q行实际的测试过E中QEasyMock 会根据方法名和参数来匚w一个预期方法的调用。EasyMock 对参数的匚w默认使用 <code>equals()</code> Ҏq行比较。这可能会引起一些问题。例如在上一章节中创建的mockStatement对象Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="65" sizcache="2"> <tbody sizset="65" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockStatement.executeQuery("SELECT * FROM sales_order_table"); expectLastCall().andStubReturn(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="66" sizcache="2">在实际的调用中,我们可能会遇?SQL 语句中某些关键字大小写的问题Q例如将 SELECT 写成 SelectQ这时在实际的测试中QEasyMock 所采用的默认匹配器认两个参数不匹配,从而造成 Mock 对象的预期方法不被调用。EasyMock 提供了灵zȝ参数匚w方式来解册个问题。如果您?mockStatement 具体执行的语句ƈ不关注,q希望所有输入的字符串都能匹配这一Ҏ调用Q您可以?<code>org.easymock.EasyMock</code> cL提供?<code>anyObject</code> Ҏ来代替参C?SQL 语句Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="66" sizcache="2"> <tbody sizset="66" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockStatement.executeQuery( anyObject() ); expectLastCall().andStubReturn(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> </p> <p><code>anyObject</code> Ҏ表示L输入值都与预期值相匚w。除?<code>anyObject</code> 以外QEasyMockq提供了多个预先定义的参数匹配器Q其中比较常用的一些有Q?/p> <ul> <li><code>aryEq(X value)</code>Q通过<code>Arrays.equals()</code>q行匚wQ适用于数l对象; <li><code>isNull()</code>Q当输入gؓNull时匹配; <li><code>notNull()</code>Q当输入g为Null时匹配; <li><code>same(X value)</code>Q当输入值和预期值是同一个对象时匚wQ? <li><code>lt(X value), leq(X value), geq(X value), gt(X value)</code>Q当输入值小于、小{于、大{于、大于预期值时匚wQ适用于数值类型; <li><code>startsWith(String prefix), contains(String substring), endsWith(String suffix)</code>Q当输入g预期值开头、包含预期倹{以预期值结时匚wQ适用于StringcdQ? <li><code>matches(String regex)</code>Q当输入g正则表达式匹配时匚wQ适用于Stringcd?</li> </ul> <p><a name="chap3.2"><span id="wmqeeuq" class="smalltitle">自定义参数匹配器</span></a></p> <p>预定义的参数匚w器可能无法满一些复杂的情况Q这时你需要定义自q参数匚w器。在上一节中Q我们希望能有一个匹配器?SQL 中关键字的大写不敏感,使用 <code>anyObject</code> 其实q不是一个好的选择。对此,我们可以定义自己的参数匹配器 SQLEquals?/p> <p>要定义新的参数匹配器Q需要实?<code>org.easymock.IArgumentMatcher</code> 接口。其中,<code>matches(Object actual)</code> Ҏ应当实现输入值和预期值的匚w逻辑Q而在 <code>appendTo(StringBuffer buffer)</code> Ҏ中,你可以添加当匚wp|旉要显C的信息。以下是 SQLEquals 实现的部分代码(完整的代码可以在 src.zip 中找刎ͼQ?/p> <br /> <a name="code001"><strong>清单5Q自定义参数匚w器SQLEquals</strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="67" sizcache="2"> <tbody sizset="67" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public class SQLEquals implements IArgumentMatcher { private String expectedSQL = null; public SQLEquals(String expectedSQL) { this.expectedSQL = expectedSQL; } ...... public boolean matches(Object actualSQL) { if (actualSQL == null && expectedSQL == null) return true; else if (actualSQL instanceof String) return expectedSQL.equalsIgnoreCase((String) actualSQL); else return false; } } </pre> </td> </tr> </tbody> </table> <br /> <p>在实C <code>IArgumentMatcher</code> 接口之后Q我们需要写一个静态方法将它包装一下。这个静态方法的实现需要将 SQLEquals 的一个对象通过 <code>reportMatcher</code> Ҏ报告lEasyMockQ?/p> <br /> <a name="code001"><strong>清单6Q自定义参数匚w?SQLEquals 静态方?/strong></a><br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="68" sizcache="2"> <tbody sizset="68" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode"> public static String sqlEquals(String in) { reportMatcher(new SQLEquals(in)); return in; } </pre> </td> </tr> </tbody> </table> <br /> <p sizset="69" sizcache="2">q样Q我们自定义?sqlEquals 匚w器就可以使用了。我们可以将上例中的 <code>executeQuery</code> Ҏ讑֮修改如下Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="69" sizcache="2"> <tbody sizset="69" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">mockStatement.executeQuery(sqlEquals("SELECT * FROM sales_order_table")); expectLastCall().andStubReturn(mockResultSet); </pre> </td> </tr> </tbody> </table> <br /> <br /> 在?<code>executeQuery("select * from sales_order_table")</code> q行Ҏ调用Ӟ该预期行为将被匹配?</p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="70" sizcache="2"> <tbody sizset="70" sizcache="1"> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="chap4"><span id="wmqeeuq" class="atitle">4Q特D的 Mock 对象cd</span></a></p> <p>到目前ؓ止,我们所创徏?Mock 对象都属?EasyMock 默认?Mock 对象cdQ它寚w期方法的调用ơ序不敏感,寚w预期的方法调用抛?AssertionError。除了这U默认的 Mock cd以外QEasyMock q提供了一些特D的 Mock cd用于支持不同的需求?/p> <p><a name="chap4.1"><span id="wmqeeuq" class="smalltitle">Strick Mock 对象</span></a></p> <p sizset="73" sizcache="2">如果 Mock 对象是通过 <code>EasyMock.createMock()</code> 或是 <code>IMocksControl.createMock()</code> 所创徏的,那么在进?verify 验证ӞҎ的调用顺序是不进行检查的。如果要创徏Ҏ调用的先后次序敏感的 Mock 对象QStrick MockQ,应该使用 <code>EasyMock.createStrickMock()</code> 来创建,例如Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="73" sizcache="2"> <tbody sizset="73" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">ResultSet strickMockResultSet = createStrickMock(ResultSet.class); </pre> </td> </tr> </tbody> </table> <br /> </p> <p sizset="74" sizcache="2">cM?createMockQ我们同样可以用 <code>IMocksControl</code> 实例来创Z?Strick Mock 对象Q? <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="74" sizcache="2"> <tbody sizset="74" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">IMocksControl control = EasyMock.createStrictControl(); ResultSet strickMockResultSet = control.createMock(ResultSet.class); </pre> </td> </tr> </tbody> </table> <br /> </p> <p><a name="chap4.2"><span id="wmqeeuq" class="smalltitle">Nice Mock 对象</span></a></p> <p>使用 <code>createMock()</code> 创徏?Mock 对象寚w预期的方法调用默认的行ؓ是抛?AssertionErrorQ如果需要一个默认返?Qnull ?false {?无效??"Nice Mock" 对象Q可以通过 <code>EasyMock</code> cL供的 <code>createNiceMock()</code> Ҏ创徏。类似的Q你也可以用 <code>IMocksControl</code> 实例来创Z?Nice Mock 对象?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="75" sizcache="2"> <tbody sizset="75" sizcache="1"> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="chap5"><span id="wmqeeuq" class="atitle">5QEasyMock 的工作原?/span></a></p> <p>EasyMock 是如何ؓ一个特定的接口动态创?Mock 对象Qƈ记录 Mock 对象预期行ؓ的呢Q其实,EasyMock 后台处理的主要原理是利用 <code>java.lang.reflect.Proxy</code> 为指定的接口创徏一个动态代理,q个动态代理,是我们在编码中用到?Mock 对象。EasyMock qؓq个动态代理提供了一?<code>InvocationHandler</code> 接口的实玎ͼq个实现cȝ主要功能是动态代理的预期行ؓ记录在某个映表中和在实际调用时从这个映表中取出预期输出。下图是 EasyMock 中主要的功能c:</p> <br /> <a name="easymock_class.gif"><strong>?QEasyMock主要功能c?/strong></a><br /> <img height="377" alt="Mock对象验证p|" src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/easymock_class.gif" width="572" /> <br /> <p>和开发h员联pL紧密的是 <code>EasyMock</code> c,q个cL供了 <code>createMock、replay、verify</code> {方法以及所有预定义的参数匹配器?/p> <p>我们知道 Mock 对象有两U创建方式:一U是通过 <code>EasyMock</code> cL供的 <code>createMock</code> Ҏ创徏Q另一U是通过 <code>EasyMock</code> cȝ <code>createControl</code> Ҏ得到一?<code>IMocksControl</code> 实例Q再p?<code>IMocksControl</code> 实例创徏 Mock 对象。其实,无论通过哪种Ҏ获得 Mock 对象QEasyMock 都会生成一?<code>IMocksControl</code> 的实例,只不q第一U方式中?<code>IMocksControl</code> 的实例对开发h员不可见而已。这?<code>IMocksControl</code> 的实例,其实是 <code>MocksControl</code> cȝ一个对象?code>MocksControl</code> cL供了 <code>andReturn、andThrow、times、createMock</code> {方法?/p> <p><code>MocksControl</code> cM包含了两个重要的成员变量Q分别是接口 <code>IMocksBehavior</code> ?<code>IMocksControlState</code> 的实例。其中,<code>IMocksBehavior</code> 的实现类 <code>MocksBehavior</code> ?EasyMock 的核心类Q它保存着一?<code>ExpectedInvocationAndResult</code> 对象的一个列表,?<code>ExpectedInvocationAndResult</code> 对象中包含着 Mock 对象Ҏ调用和预期结果的映射?code>MocksBehavior</code> cL供了 <code>addExpected</code> ?<code>addActual</code> Ҏ用于d预期行ؓ和实际调用?/p> <p><code>MocksControl</code> cM包含的另一个成员变量是 <code>IMocksControlState</code> 实例?code>IMocksControlState</code> 拥有两个不同的实现类Q?code>RecordState</code> ?<code>ReplayState</code>。顾名思义Q?code>RecordState</code> ?Mock 对象?Record 状态时的支持类Q它提供?<code>invoke</code> Ҏ?Record 状态下的实现。此外,它还提供?<code>andReturn、andThrow、times</code> {方法的实现?code>ReplayState</code> ?Mock 对象?Replay 状态下的支持类Q它提供?<code>invoke</code> Ҏ?Replay 状态下的实现。在 ReplayState 中,<code>andReturn、andThrow、times</code> {方法的实现都是抛出IllegalStateExceptionQ因为在 Replay 阶段Q开发h员不应该再调用这些方法?/p> <p>当我们调?<code>MocksControl</code> ?<code>createMock</code> ҎӞ该方法首先会生成一?<code>JavaProxyFactory</code> cȝ对象?code>JavaProxyFactory</code> 是接?<code>IProxyFactory</code> 的实现类Q它的主要功能就是通过 <code>java.lang.reflect.Proxy</code> Ҏ定的接口创徏动态代理实例,也就是开发h员在外部看到?Mock 对象?/p> <p>在创建动态代理的同时Q应当提?<code>InvocationHandler</code> 的实现类?code>MockInvocationHandler</code> 实现了这个接口,它的 <code>invoke</code> Ҏ主要的功能是Ҏ Mock 对象状态的不同而分别调?<code>RecordState</code> ?<code>invoke</code> 实现或是 <code>ReplayState</code> ?<code>invoke</code> 实现?/p> <p><a name="chap5.1"><span id="wmqeeuq" class="smalltitle">创徏 Mock 对象</span></a></p> <p>下图是创?Mock 对象的时序图Q?/p> <br /> <a name="createmock_seq.gif"><strong>?Q创?Mock 对象时序?/strong></a><br /> <img height="234" alt="创徏 Mock 对象时序? src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/createmock_seq.gif" width="572" /> <br /> <p>?<code>EasyMock</code> cȝ <code>createMock</code> Ҏ被调用时Q它首先创徏一?<code>MocksControl</code> 对象Qƈ调用该对象的 <code>createMock</code> Ҏ创徏一?<code>JavaProxyFactory</code> 对象和一?<code>MockInvocationHandler</code> 对象?code>JavaProxyFactory</code> 对象?<code>MockInvocationHandler</code> 对象作ؓ参数Q通过 <code>java.lang.reflect.Proxy</code> cȝ <code>newProxyInstance</code> 静态方法创Z个动态代理?/p> <p><a name="chap5.2"><span id="wmqeeuq" class="smalltitle">记录 Mock 对象预期行ؓ</span></a></p> <p>记录 Mock 的预期行为可以分Z个阶D:预期Ҏ的调用和预期输出的设定。在外部E序中获得的 Mock 对象Q其实就是由 <code>JavaProxyFactory</code> 创徏的指定接口的动态代理,所有外部程序对接口Ҏ的调用,都会指向 <code>InvocationHandler</code> 实现cȝ <code>invoke</code> Ҏ。在 EasyMock 中,q个实现cL <code>MockInvocationHandler</code>。下图是调用预期Ҏ的时序图Q?/p> <br /> <a name="recordstate_seq1.gif"><strong>?Q调用预期方法时序图</strong></a><br /> <img height="261" alt="调用预期Ҏ时序? src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/recordstate_seq1.gif" width="572" /> <br /> <p>?<code>MockInvocationHandler</code> ?<code>invoke</code> Ҏ被调用时Q它首先通过 <code>reportLastControl</code> 静态方法将 Mock 对象对应?<code>MocksControl</code> 对象报告l?<code>LastControl</code> c,<code>LastControl</code> cd该对象保存在一?ThreadLocal 变量中。接着Q?code>MockInvocationHandler</code> 创Z?Invocation 对象Q这个对象将保存预期调用?Mock 对象、方法和预期参数?/p> <p>在记?Mock 对象预期行ؓӞMock 对象的状态是 Record 状态,因此 <code>RecordState</code> 对象?<code>invoke</code> Ҏ被调用。这个方法首先调?<code>LastControl</code> ?<code>pullMatchers</code> Ҏ获取参数匚w器。如果您q记得自定义参数匚w器的q程Q应该能惌v参数匚w器被调用时会实现类的实例报告给 EasyMockQ而这个实例最l保存在 <code>LastControl</code> 中。如果没有指定参数匹配器Q默认的匚w器将会返回给 <code>RecordState</code>?/p> <p>Ҏ <code>Invocation</code> 对象和参数匹配器Q?code>RecordState</code> 创Z?<code>ExpectedInvocation</code> 对象q保存下来?/p> <p sizset="78" sizcache="2">在对预期Ҏq行调用之后Q我们可以对该方法的预期输出q行讑֮。我们以 <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="78" sizcache="2"> <tbody sizset="78" sizcache="1"> <tr> <td class="code-outline"> <pre class="displaycode">expectLastCall().andReturn(X value).times(int times) </pre> </td> </tr> </tbody> </table> <br /> <br /> Z说明。如?<code>times</code> Ҏ未被昑ּ的调用,EasyMock 会默认作?<code>times(1)</code> 处理。下图是讑֮预期输出的时序图Q?</p> <br /> <a name="recordstate_seq2.gif"><strong>?Q设定预期输出时序图</strong></a><br /> <img height="340" alt="讑֮预期输出时序? src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/recordstate_seq2.gif" width="572" /> <br /> <p>在预期方法被调用ӞMock 对象对应?<code>MocksControl</code> 对象引用已经记录?<code>LastControl</code> 中,<code>expectLastCall</code> Ҏ通过调用 <code>LastControl</code> ?<code>lastControl</code> Ҏ可以获得q个引用?code>MocksControl</code> 对象?<code>andReturn</code> Ҏ?Mock 对象 Record 状态下会调?<code>RecordState</code> ?<code>andReturn</code> ҎQ将讑֮的预期输Z <code>Result</code> 对象的Ş式记录下来,保存?<code>RecordState</code> ?lastResult 变量中?/p> <p>?<code>MocksControl</code> ?<code>times</code> Ҏ被调用时Q它会检?<code>RecordState</code> ?lastResult 变量是否为空。如果不为空Q则?lastResult 和预期方法被调用时创建的 <code>ExpectedInvocation</code> 对象一P作ؓ参数传递给 <code>MocksBehavior</code> ?<code>addExpected</code> Ҏ?code>MocksBehavior</code> ?<code>addExpected</code> Ҏ这些信息保存在数据列表中?/p> <p><a name="chap5.3"><span id="wmqeeuq" class="smalltitle">?Replay 状态下调用 Mock 对象Ҏ</span></a></p> <p><code>EasyMock</code> cȝ <code>replay</code> Ҏ可以?Mock 对象切换?Replay 状态。在 Replay 状态下QMock 对象根据之前的讑֮q回预期输出。下图是 Replay 状态下 Mock 对象Ҏ调用的时序图Q?/p> <br /> <a name="replaystate_seq.gif"><strong>?Q调?Mock 对象Ҏ时序?/strong></a><br /> <img height="244" alt="调用 Mock 对象Ҏ时序? src="http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/replaystate_seq.gif" width="572" /> <br /> <p>?Replay 状态下Q?code>MockInvocationHandler</code> 会调?<code>ReplayState</code> ?<code>invoke</code> Ҏ。该Ҏ会把 Mock 对象通过 <code>MocksBehavior</code> ?<code>addActual</code> Ҏd到实际调用列表中Q该列表?<code>verify</code> Ҏ被调用时被用到。同Ӟ<code>addActual</code> Ҏ会根据实际方法调用与预期Ҏ调用q行匚wQ返回对应的 <code>Result</code> 对象。调?<code>Result</code> 对象?<code>answer</code> Ҏ可以获取该Ҏ调用的输出?/p> <br /> <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="79" sizcache="2"> <tbody sizset="79" sizcache="1"> <tr> <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /> <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td> </tr> </tbody> </table> <br /> <br /> <p><a name="chap6"><span id="wmqeeuq" class="atitle">6Q?EasyMock q行单元试结</span></a></p> <p>如果您需要在单元试中构?Mock 对象来模拟协同模块或一些复杂对象,EasyMock 是一个可以选用的优U框架。EasyMock 提供了简便的Ҏ创徏 Mock 对象Q通过定义 Mock 对象的预期行为和输出Q你可以讑֮?Mock 对象在实际测试中被调用方法的q回倹{异常抛出和被调用次数。通过创徏一个可以替代现有对象的 Mock 对象QEasyMock 使得开发h员在试时无需~写自定义的 Mock 对象Q从而避免了额外的编码工作和因此引入错误的机会?/p> <img src ="http://www.aygfsteel.com/shiwenfeng/aggbug/303153.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/shiwenfeng/" target="_blank">shiwf</a> 2009-11-21 17:34 <a href="http://www.aygfsteel.com/shiwenfeng/archive/2009/11/21/303153.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <a href="http://www.aygfsteel.com/" title="狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频">狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频</a> </div> </footer> վ֩ģ壺 <a href="http://" target="_blank">٤ʦ</a>| <a href="http://" target="_blank">ʡ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">Զ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ٹ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ӳ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank">ˮ</a>| <a href="http://" target="_blank">ī񹤿</a>| <a href="http://" target="_blank">º</a>| <a href="http://" target="_blank">Ҧ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ľ˹</a>| <a href="http://" target="_blank">Դ</a>| <a href="http://" target="_blank">Ҧ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">¡</a>| <a href="http://" target="_blank">ຣʡ</a>| <a href="http://" target="_blank">׶</a>| <a href="http://" target="_blank">Ϫ</a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>