Java Opensources for Web Development Part I:
Chapter 4 在 Java 中進行各種單元測試
Lession 16 : jMock
* Category: Test
* Project Name: jMock
* WebSite: https://www.jmock.org/
* License: jMock project license
* Last version: 2.0.0 stable, 2.1.0 (rc3)
當施行 Test Driven Development 的時候,我們未必會有實體的環境與資料源可以進行測試,所以我們必須模擬部分環境,與資料回應的狀態,如果僅僅使用 JUnit,我們產出的資料其實是回應在每個實作的 Object class 之中。
例如 UserDAOImpl 是撰寫 存取資料庫的實作。
package com.softleader.unittest;
public class UserDAOImpl implements UserDAO{
public UserDAOImpl(){
// 連結資料庫
}
public void createUser(User user) {
// insert into user ( id, name ) values ( $id, $name );
}
public User getUser(long id) {
// select * from user where id = $id;
return null;
}
public void removeUser(long id) {
// delete user where id = $id
}
public void setUser(User user) {
// update user set name = $name where id = $id
}
}
User 是一個簡單的 ValueObject,目前設定簡單的屬性有 id 與 name。
package com.softleader.unittest;
import org.apache.commons.lang.builder.ToStringBuilder;
public class User {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
如果僅使用 JUnit 測試 getUser(), 我們就需要把 UserDAOImpl 撰寫成為
public User getUser(long id) {
User user = new User();
user.setId(1);
user.setName("jini");
return user;
}
不過,這方法在未來上線之前,變得還需要去修改 UserDAOImpl 轉為存取資料庫,所以我們會希望能夠在測試環境中,利用 interface 的自我實作方式,來進行模擬測試。
UserBusinessDelegate 是一支呼叫 DAO 的程式,我們可以把模擬物件的 DAO 放到 Delegate 之中,讓 Delegate 進行模擬測試。在實際環境時,則是將真實的 DAO inject進來即可,這也是 IoC 的重要應用之一。
package com.softleader.unittest;
public class UserBusinessDelegate {
private UserDAO dao;
public void setDAO(UserDAO dao) {
this.dao = dao;
}
public void deleteUser(long id) {
dao.removeUser(id);
}
public User getUser(long id) {
return dao.getUser(id);
}
public void insertUser(User user) {
dao.createUser(user);
}
public void updateUser(User user) {
dao.setUser(user);
}
}
package com.softleader.unittest;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
public class UserBussinessDelegateTest {
// Mockery 初始化
Mockery context = new JUnit4Mockery();
@Test
public void testGetUser() {
// 利用 context 呼叫 UserDAO interface 產生模擬物件
final UserDAO dao = context.mock(UserDAO.class);
// 將 模擬 DAO 放到 Delegate 之中
UserBusinessDelegate delegate = new UserBusinessDelegate();
delegate.setDAO(dao);
// 建立一個模擬物件
final User testuser = new User();
testuser.setId(1);
testuser.setName("jini");
// 設定期望的處理情況
context.checking(new Expectations() {{
one (dao).getUser(1);
will(returnValue(testuser));
}});
System.out.println(delegate.getUser(1));
}
}
最後就會印出
com.softleader.unittest.User@929206[id=1,name=jini]
換句話說,我們利用 jMock 創造出一個 User模擬物件 [id=1, name=jini],在 delegate 呼叫 DAO 的時候,就會回傳該物件。
在 jMock 的實作之中,比較簡單的回傳值,就直接使用 returnValue 即可,但是如果是回傳多筆資料,就需要利用 returnIterator() 的方式,更特殊的情況有可能是不同次呼叫時,回有不同的回傳值,就可以利用 onConsecutiveCalls(returnValue(“firsttime”), returnValue(“secondtime”), returnValue(“thirdtime”)) 的方式來設定依序的回傳值。
回傳的情況除了數值與物件或是集合之外,當然也有可能是例外狀況。僅需要簡單地使用 will(throwException(new SomeException(“you get an exception”))); 的方式來檢測例外狀況是否發生。
jMock 支援許多呼叫的方式,可以參考 jMock Cookbook (https://jmock.codehaus.org/cookbook.html ) 仔細研究之即可。
另外,在 SpringFramework 之中應用 jMock 可以參考
https://appfuse.dev.java.net/TDDWithAppFuse.pdf