Dummy 物件 是一種用來隔離真實環境,使受測程式不受其它物件或外在環境影響的方式。Dummy物件之所以為Dummy,就是它通常沒有複雜的行為,只單純傳回必要的 值或物件,供受測程式可以運行。
相較而言,Mock物件就複雜了一些,Mock物件的作用與 Dummy物件類似,隔離真實環境,使受測程式不受其它物件或外在環境影響,所不同的是,Mock物件模擬了真實物件的行為,真實物件被操作後應有什麼狀態變化, Mock物件就會模擬類似的變化。
舉個例子來說,你設計了以下的BookmarkService:
package cc.openhome.model;
import cc.openhome.dao.BookmarkDAO;
// 其中Bookmark物件會實作equals()與hashCode()
public class BookmarkService {
    private BookmarkDAO dao;
    public BookmarkService(BookmarkDAO dao) {
        this.dao = dao;
    }
    public void add(Bookmark bookmark) {
        if(!dao.get().contains(bookmark)) {
            dao.add(bookmark);
        }
    }
}
BookmarkService 委託BookmarkDAO來作資料庫增刪查找的動作:
package cc.openhome.dao;
import cc.openhome.model.Bookmark;
import java.util.List;
public interface BookmarkDAO {
    public List<Bookmark> get();
    public void add(Bookmark bookmark);
    public void delete(Bookmark bookmark);
}
檢查目前是否有重複書籤記錄的商務邏輯由BookmarkService負責,假設這邊的作法是從BookmarkDAO取得所有書籤並比較,若無重複再新增書籤 至資料庫。顯然這邊要能測試BookmarkService,就一定得有個BookmarkDAO實作,但你的目的僅在測試 BookmarkService的add(),而不是要連同BookmarkDAO實作物件一同測試。
因此在這邊你要有個假貨,這個假貨會有BookmarkDAO的增刪查找行為,但又不是真正與資料庫作增刪查找,因此你可以這麼設計:
package test.cc.openhome;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import cc.openhome.dao.BookmarkDAO;
import cc.openhome.model.Bookmark;
import cc.openhome.model.BookmarkService;
class MockDAO implements BookmarkDAO {
    private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
    public void add(Bookmark bookmark) {
        bookmarks.add(bookmark);
    }
    public void delete(Bookmark bookmark) {
        bookmarks.remove(bookmark);
    }
    public List<Bookmark> get() {
        return bookmarks;
    }
}
public class BookmarkServiceTest {
    @Test
    public void testAdd() {
        BookmarkDAO mockDAO = new MockDAO();
        Bookmark bookmark1 = 
            new Bookmark("testURL1", "testTitle1", "testCategory1");
        Bookmark bookmark2 = 
            new Bookmark("testURL2", "testTitle2", "testCategory2");
        mockDAO.add(new Bookmark("testURL1", "testTitle1", "testCategory1"));
        
        BookmarkService service = new BookmarkService(mockDAO);
        service.add(bookmark1);
        assertEquals(1, mockDAO.get().size());
        
        service.add(bookmark2);
        assertEquals(2, mockDAO.get().size());
        assertEquals(bookmark2, mockDAO.get().get(1));
    }
}
實際上需要Mock物件進行測試時,有一些現成框架可以使 用,像是EasyMock或JMock,這之後會再說明。
再觀察Dummy 物件 中的例子,其實你要的傳回一個InputStream, 原本該文中範例的作法最後的作法是,實作一個DummyURLConnection作為Dummy物件,傳回測試所需的InputStream,其實那個傳回的 InputStream,就是個Mock物件的概念,因為它並不是真正代表 HTTP連結的輸入串流,只不過用來與HttpHelper的getContent()作互動,你真正想測試的,其實是取回的InputStream與 getContent()中其它部份,實作是否正確。
你可以修改一下HttpHelper:
package cc.openhome;
import java.io.*;
import java.net.URL;
public class HttpHelper {
    public String getContent(URL url) throws IOException {
        InputStream input = createInputStream(url);
        StringWriter writer = new StringWriter();
        byte[] data = new byte[2048];
        int length = -1;
        while((length = input.read(data)) != -1) {
            writer.write(new String(data, 0, length));
        }
        input.close();
        writer.close();
        return writer.toString();
    }
    protected InputStream createInputStream(URL url) throws IOException {
        return url.openStream();
    }
}
為了要能進行測 試,傳回InputStream的假貨,你的測試程式可以如下撰寫:
package test.cc.openhome;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.junit.Test;
import cc.openhome.HttpHelper;
class TestForHttpHelper extends HttpHelper {
    @Override
    protected InputStream createInputStream(URL url) throws IOException {
        // 傳回的 InputStream 就是 Mock 物件的概念
        return new ByteArrayInputStream(new String("success").getBytes());
    }
}
public class HttpHelperTest {
    @Test
    public void testGetContent() throws Exception {
        HttpHelper helper = new TestForHttpHelper();
        String expected = "success";
        String result = helper.getContent(new URL("http://localhost:8080"));
        assertEquals(expected, result);
    }
}

