下面是示例代碼中的一個接口 SalesOrder,它的實現(xiàn)類 SalesOrderImpl 的主要功能是從數(shù)據(jù)庫中讀取一個 Sales Order 的 Region 和 Total Price,并根據(jù)讀取的數(shù)據(jù)計算該 Sales Order 的 Price Level(完整的實現(xiàn)代碼都可以在 src.zip 中找到):
清單2:SalesOrder 接口
public interface SalesOrder
{
……
public void loadDataFromDB(ResultSet resultSet) throws SQLException;
public String getPriceLevel();
}
其實現(xiàn)類 SalesOrderImpl 中對 loadDataFromDB 的實現(xiàn)如下:
清單3:SalesOrderImpl 實現(xiàn)
public class SalesOrderImpl implements SalesOrder
{
......
public void loadDataFromDB(ResultSet resultSet) throws SQLException
{
orderNumber = resultSet.getString(1);
region = resultSet.getString(2);
totalPrice = resultSet.getDouble(3);
}
......
}
方法 loadDataFromDB 讀取了 ResultSet 對象包含的數(shù)據(jù)。當我們將之前定義的 Mock 對象調整為 Replay 狀態(tài),并將該對象作為參數(shù)傳入,那么 Mock 對象的方法將會返回預先定義的預期返回值。完整的 TestCase 如下:
清單4:完整的TestCase
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();
}
}
}
在這個示例中,我們首先創(chuàng)建了 ResultSet 的 Mock 對象 moResultSet,并記錄該 Mock 對象的預期行為。之后我們調用了 control.replay(),將 Mock 對象的狀態(tài)置為 Replay 狀態(tài)。 在實際的測試階段,Sales Order 對象的 loadDataFromDB 方法調用了 mockResultSet 對象的 getString 和 getDouble 方法讀取 mockResultSet 中的數(shù)據(jù)。Sales Order 對象根據(jù)讀取的數(shù)據(jù)計算出 Price Level,并和預期輸出進行比較。
對 Mock 對象的行為進行驗證
在利用 Mock 對象進行實際的測試過程之后,我們還有一件事情沒有做:對 Mock 對象的方法調用的次數(shù)進行驗證。
為了驗證指定的方法調用真的完成了,我們需要調用 verify 方法進行驗證。和 replay 方法類似,您需要根據(jù) Mock 對象的生成方式來選用不同的驗證方式。如果 Mock 對象是由 org.easymock.EasyMock 類提供的 createMock 靜態(tài)方法生成的,那么我們同樣采用 EasyMock 類的靜態(tài)方法 verify 進行驗證:
verify(mockResultSet);
如果Mock對象是有 IMocksControl 接口所提供的 createMock 方法生成的,那么采用該接口提供的 verify 方法,例如第1節(jié)中的 IMocksControl 實例 control:
control.verify();
將對 control 實例所生成的 Mock 對象 mockConnection、mockStatement 和 mockResultSet 等進行驗證。如果將上例中 expectLastCall().andReturn(false).times(1) 的預期次數(shù)修改為2,在 Eclipse 中將可以看到:
圖3:Mock對象驗證失敗
Mock 對象的重用
為了避免生成過多的 Mock 對象,EasyMock 允許對原有 Mock 對象進行重用。要對 Mock 對象重新初始化,我們可以采用 reset 方法。和 replay 和 verify 方法類似,EasyMock 提供了兩種 reset 方式:(1)如果 Mock 對象是由 org.easymock.EasyMock 類中的靜態(tài)方法 createMock 生成的,那么該 Mock 對象的可以用 EasyMock 類的靜態(tài)方法 reset 重新初始化;(2)如果 Mock 方法是由 IMocksControl 實例的 createMock 方法生成的,那么該 IMocksControl 實例方法 reset 的調用將會把所有該實例創(chuàng)建的 Mock 對象重新初始化。