使用如下方式在Maven中添加EasyMock的依賴:
<
dependency
>
<
groupId
>org.easymock</
groupId
>
<
artifactId
>easymock</
artifactId
>
<
version
>3.2</
version
>
<
scope
>test</
scope
>
</
dependency
>
|
EasyMock使用動態代理實現模擬對象創建,其基本步驟為以下四步:
以數據庫應用為例的被測試代碼如下:
public
class
UserServiceImpl{
private
UserDao dao;
public
User query(String id)
throws
Exception{
try
{
return
dao.getById(id);
}
catch
(Exception e){
throw
e;
}
return
null
;
}
}
public
class
UserDao{
public
User getById(String id)
throws
Exception{
try
{
return
……;
}
catch
(Exception e){
throw
e;
}
return
null
;
}
}
|
現在希望對UserServiceImpl進行測試,而UserDao開發組只給出接口,尚未完成功能實現。
使用Mock對UserDao進行模擬來測試UserServiceImpl。
(1).基本的測試代碼如下:
public
class
UserServiceImplTest {
@Test
public
void
testQuery() {
User expectedUser =
new
User();
user.setId(“
1001
”);
UserDao mock = EasyMock.createMock(UserDao.
class
);
//創建Mock對象
Easymock.expect(mock.getById(
"1001"
)).andReturn(expectedUser);
//錄制Mock對象預期行為
Easymock.replay(mock);
//重放Mock對象,測試時以錄制的對象預期行為代替真實對象的行為
UserServiceImpl service =
new
UserServiceImpl();
service.setUserDao(mock);
user user = service.query(
"1001"
);
//調用測試方法
assertEquals(expectedUser, user);
//斷言測試結果
Easymock.verify(mock);
//驗證Mock對象被調用
}
}
|
注意:
在EasyMock3.0之前,org.easymock.EasyMock使用JDK的動態代理實現Mock對象創建,因此只能針對接口進行Mock,org.easymock.classextension.EasyMock使用CGLIB動態代理創建Mock對象,可以針對普通類進行Mock。
在EasyMock3.0之后,org.easymock.classextension.EasyMock被廢棄,使用org.easymock.EasyMock可以針對接口和普通類進行Mock對象創建。
(2).調用測試設定:
如果想測試UserServiceImpl調用了UserDao的getById方法3次,則使用如下代碼即可:
Easymock.expect(mock.getById(
"1001"
)).andReturn(exceptUser).times(
3
);
|
(3).方法異常:
如果想測試UserServiceImpl在調用UserDao的getById方法時發生異常,可以使用如下代碼:
Easymock.expect(mock.getById(
"1001"
)).andThrow(
new
RuntimeException());
|
在測試UserServiceImpl時就可以使用try-catch捕獲Mock的異常。
(4).基本參數匹配:
上面的方法在Mock UserDao的getById方法時傳入了“0001”的預期值,這種方式是精確參數匹配,如果UserServiceImpl在調用是傳入的參數不是“0001”就會發生Unexpect method的Mock異常,可以使用下面的方法在Mock時進行參數匹配:
Easymock.expect(mock.getById(Easymock.isA(String.
class
))).andReturn(exceptedUser).times(
3
);
|
isA()方法會使用instanceof進行參數類型匹配,類似的方法還有anyInt(),anyObject(), isNull(),same(), startsWith()......
(5).數組類型參數匹配:
如果UserServiceImpl在調用UserDao的方法時傳入的參數是數組,代碼如下:
public
class
UserServiceImpl{
private
UserDao dao;
public
List<String> queryNames(String[] ids)
throws
Exception{
try
{
return
dao.getNames(ids);
}
catch
(Exception e){
throw
e;
}
return
null
;
}
}
public
class
UserDao{
public
List<String> getNames(String[] ids)
throws
Exception{
try
{
return
……;
}
catch
(Exception e){
throw
e;
}
return
null
;
}
}
此時有兩種辦法來進行參數匹配:a.數組必須和測試給定的一致:
[java] view plain copy
Easymock.expect(mock.getNames(EasyMock.aryEq(testIds))).andReturn(exceptedNames);
b.不考慮測試數組內容:[java] view plain copy
Easymock.expect(mock.getNames(EasyMock.isA(String[].
class
))).andReturn(exceptedNames);
|
(
6
).
void
方法Mock:如果要Mock的方法是無返回值類型,例子如下:
[java] view plain copy
public
class
UserDao {
public
void
updateUserById(String id)
throws
Exception{
try
{
update…
}
catch
(Exception e){
throw
e;
}
}
}
a.正常Mock代碼如下:[java] view plain copy
mock.updateUserById(“TestId”);
EasyMock.expectLastCall().anytimes();
b.模擬發生異常的Mock代碼如下:[java] view plain copy
mock.updateUserById(“TestId”);
EasyMock.expectLastCall().andThrow(
new
RuntimeException()).anytimes();
(
7
).多次調用返回不同值的Mock:對於迭代器類型的遍歷代碼來說,需要在不同調用時間返回不同的結果,以JDBC結果集為例代碼如下:
[java] view plain copy
public
List<String> getUserNames ()
throws
Exception{
List<String> usernames =
new
ArrayList<String>();
ResultSet rs = pstmt.executeQuery(query);
try
{
while
(rs.next()){
usernames.add(rs.getString(
2
));
}
}
catch
(SQLException e) {
throw
e;
}
}
在Mock結果集的next方法時如果總返回
true
,則代碼就會陷入死循環,如果總返回
false
則代碼邏輯根本無法執行到循環體內。正常的測試邏輯應該是先返回幾次
true
執行循環體,然后在返回
false
退出循環,使用Mock可以方便模擬這種預期的行為,代碼如下:
[java] view plain copy
EasyMock.expect(rs.next()).andReturn(
true
).times(
2
).andReturn(
false
).times(
1
);
更多的關於EasyMock的用法,請參考EasyMock官方文檔:
|
