public interface SalesOrder { …… public void loadDataFromDB(ResultSet resultSet) throws SQLException; public String getPriceLevel(); } |
如果我们对 getPriceLevel
方法进行测试,就可以将该方法在正确运行下的预期输出抽象成 Mock 对象,并配置如下:
清单7:配置预期输出
<mockConfig> <mockObjects> <mockObject name="mockSalesOrder" mockedClass="xmleasymock.demo.test.SalesOrder" /> </mockObjects> <mockBehaviors> <mockBehavior mockObject="mockSalesOrder" method="getPriceLevel"> <paramValues /> <ctrlOptions> <ctrlOption option="andReturn" value="expected result 1" times="1" /> <ctrlOption option="andReturn" value="expected result 2" times="1" /> ...... </ctrlOptions> </mockBehavior> </mockBehaviors> </mockConfig> |
与在代码中动态构建 Mock 对象不同,XMLEasyMock 是在配置文件的解析过程中动态生成 Mock 对象的。因此,如果用户需要使用 Mock 对象,需要从解析模块中获取。XMLEasyMock 提供了一个工具类 EasyMockUtil
,这个工具类提供了 findMockObjectByName
方法用于返回 Mock 对象。该方法的输入参数就是在配置文件的 <mockObject> 元素中配置的 name 属性。另外,我们在上文中提到,EasyMockUtil
提供了方法 loadConfig
用于装入配置文件,该方法的输入参数就是配置文件的路径。
在 XMLEasyMock 提供的测试代码(SalesOrderTestCase.java)中,我们对 ResultSet
接口进行了模拟,从而对 SalesOrder
的 getPriceLevel
进行测试。在完成相关 Mock 对象的配置之后,我们可以通过 XMLEasyMock 提供的功能对测试用例实现如下:
清单8:完整的 TestCase
public class SalesOrderTestCase extends TestCase { public void testAfterConfig() { try { EasyMockUtil.loadConfig("/xmleasymock/demo/properties/mockConfig.xml"); DBUtility mockDBUtility = (DBUtility) EasyMockUtil.findMockObjectByName("mockDBUtility"); Connection conn = mockDBUtility.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from sales_order"); SalesOrder realOrder = new SalesOrderImpl(); SalesOrder mockOrder = (SalesOrder) EasyMockUtil.findMockObjectByName("mockSalesOrder"); while (rs.next()) { realOrder.loadDataFromDB(rs); assertEquals(realOrder.getPriceLevel(), mockOrder.getPriceLevel()); } EasyMockUtil.verify(); } catch (Exception e) { e.printStackTrace(); } } } |
通常,在数据库连接模块中,开发人员都会开发一个类似于 DBUtitlity
的实现来获取数据库连接以及释放数据库资源。在我们的测试用例中,这不是必需的,但为了对真实的开发和测试环境进行模拟,我们也在 mockConfig.xml 中对 DBUtiltiy, Connection
和 Statement
等接口进行了配置。我们通过 DBUtility Mock 对象的名称 "mockDBUtility" 获得 mockDBUtility 对象,并通过它得到 ResultSet
的 Mock 对象。在对 getPriceLevel
方法进行测试时,我们将预期结果抽象成 Mock 对象,它在配置文件中的名称是 mockSalesOrder。我们通过这个名称获得预期结果的 Mock 对象。最后,我们将实际结果和预期结果进行比对,从而完成测试。
我们通过配置文件对 Mock 对象进行定义,实现了测试数据和代码的分离,从而避免了将数据编码在代码中所带来的一系列不便。当测试数据或是测试用例发生变化时,开发或部署人员只需对配置文件作出改动,而不用修改测试代码和重新编译、部署,降低了测试用例发生变化所带来的工作量和时间花销。本文基于 EasyMock 实现了通过配置文件定义Mock对象的机制。实际上,读者可以基于任何的自己熟悉的 xMock 项目来实现这里的思想。