<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    閑人野居
    好好學習,天天向上
    posts - 57,  comments - 137,  trackbacks - 0

    關于單元測試,模擬對象一直是不可缺少的,尤其對于復雜的應用來說。
    ?????? 這么多的模擬對象框架中,個人覺得比較好用的當屬EasyMock了。當然JMock也不錯。
    ?????? 下面簡單介紹一下EasyMock?。(基本翻譯EasyMock的文檔,可能有些地方不是很恰當)
    ?????
    ???????EasyMock 2
    主要用于給指定的接口提供模擬對象。

    模擬對象只是模擬領域代碼直接的部分行為,能檢測是否他們如定義中的被使用。使用 Mock 對象,來模擬合作接口,有助于隔離測試相應的領域類。

    創建和維持 Mock 對象經常是繁瑣的任務,并且可能會引入錯誤。 EasyMock 2 動態產生 Mock 對象,不需要創建,并且不會產生代碼。

    有利的方面:

    不需要手工寫類來處理 mock 對象。

    支持安全的重構 Mock 對象:測試代碼不會在運行期打斷當重新命名方法或者更改方法參數。

    支持返回值和例外。

    支持檢察方法調用次序,對于一個或者多個 Mock 對象。

    不利的方面: 2.0 僅使用于 java 2 版本 5.0 或者以上
    ????

    ??? 以一個例子來說明如何使用EasyMock:
    ???假設有一個合作接口Collaborator:
    ???????????
    package org.easymock.samples;
    ?
    public interface Collaborator {
    ??? void documentAdded(String title);
    ??? void documentChanged(String title);
    ??? void documentRemoved(String title);
    ??? byte voteForRemoval(String title);
    ??? byte[] voteForRemovals(String[] title);
    }

    我們主要的測試類為:
    package org.easymock.samples;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    public class ClassUnderTest {
    ??? private Set<Collaborator> listeners = new HashSet<Collaborator>();
    ??? private Map<String, byte[]> documents = new HashMap<String, byte[]>();
    ??? public void addListener(Collaborator listener) {
    ??????? listeners.add(listener);
    ??? }
    ??? public void addDocument(String title, byte[] document) {
    ??????? boolean documentChange = documents.containsKey(title);
    ??????? documents.put(title, document);
    ??????? if (documentChange) {
    ??????????? notifyListenersDocumentChanged(title);
    ??????? } else {
    ??????????? notifyListenersDocumentAdded(title);
    ??????? }
    ??? }
    ??? public boolean removeDocument(String title) {
    ??????? if (!documents.containsKey(title)) {
    ??????????? return true;
    ??????? }
    ??????? if (!listenersAllowRemoval(title)) {
    ??????????? return false;
    ??????? }
    ??????? documents.remove(title);
    ??????? notifyListenersDocumentRemoved(title);
    ??????? return true;
    ??? }
    ??? public boolean removeDocuments(String[] titles) {
    ??????? if (!listenersAllowRemovals(titles)) {
    ??????????? return false;
    ??????? }
    ??????? for (String title : titles) {
    ??????????? documents.remove(title);
    ??????????? notifyListenersDocumentRemoved(title);
    ??????? }
    ??????? return true;
    ??? }
    ??? private void notifyListenersDocumentAdded(String title) {
    ??????? for (Collaborator listener : listeners) {
    ??????????? listener.documentAdded(title);
    ??????? }
    ??? }
    ??? private void notifyListenersDocumentChanged(String title) {
    ??????? for (Collaborator listener : listeners) {
    ??????????? listener.documentChanged(title);
    ??????? }
    ??? }
    ??? private void notifyListenersDocumentRemoved(String title) {
    ??????? for (Collaborator listener : listeners) {
    ??????????? listener.documentRemoved(title);
    ??????? }
    ??? }
    ??? private boolean listenersAllowRemoval(String title) {
    ??????? int result = 0;
    ??????? for (Collaborator listener : listeners) {
    ??????????? result += listener.voteForRemoval(title);
    ??????? }
    ??????? return result > 0;
    ??? }
    ??? private boolean listenersAllowRemovals(String[] titles) {
    ??????? int result = 0;
    ??????? for (Collaborator listener : listeners) {
    ??????????? result += listener.voteForRemovals(titles);
    ??????? }
    ??????? return result > 0;
    ??? }
    }

    第一個Mock 對象

    我們將創建test case 并且圍繞此理解相關的EasyMock 包的功能。第一個測試方法,用于檢測是否刪除一個不存在的文檔,不會發通知給合作類。
    ??????????
    package org.easymock.samples;
    ?
    import junit.framework.TestCase;
    ?
    public class ExampleTest extends TestCase {
    ?
    ??? private ClassUnderTest classUnderTest;
    ??? private Collaborator mock;
    ?
    ??? protected void setUp() {
    ??????? classUnderTest = new ClassUnderTest();
    ??????? classUnderTest.addListener(mock);
    ??? }
    ?
    ??? public void testRemoveNonExistingDocument() {??? 
    ????????// This call should not lead to any notification
    ???? ???// of the Mock Object: 
    ????????classUnderTest.removeDocument("Does not exist");
    ??? }
    }
    ????對于多數測試類,使用EasyMock 2,我們只需要靜態引入org.easymock.EasyMock的方法。??????
    ?

    import static org.easymock.EasyMock.*;

    import junit.framework.TestCase;

    ?

    public class ExampleTest extends TestCase {

    ?

    ??? private ClassUnderTest classUnderTest;

    ??? private Collaborator mock;

    ???

    }

    ?????

    為了取得Mock 對象,需要:

    l???????? 創建Mock 對象從需要模擬的接口

    l???????? 記錄期待的行為

    l???????? 轉換到Mock對象,replay狀態。

    例如:?????
    ?
    protected void setUp() {
    ??? ????mock = createMock(Collaborator.class); // 1
    ??????? classUnderTest = new ClassUnderTest();
    ??????? classUnderTest.addListener(mock);
    ??? }

    ?public void testRemoveNonExistingDocument() {
    ??????? // 2 (we do not expect anything)
    ??????? replay(mock); // 3
    ??????? classUnderTest.removeDocument("Does not exist");
    ??? }
    ??

    在執行第三步后,mock Collaborator接口的Mock對象,并且期待沒有什么調用。這就意味著,如果我們改變ClassUnderTest去調用此接口的任何方法,則Mock對象會拋出AssertionError

    ????????
    ?
    java.lang.AssertionError: 
    ??Unexpected method call documentRemoved("Does not exist"):
    ??? at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
    ??? at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
    ??? at $Proxy0.documentRemoved(Unknown Source)
    ??? at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)
    ??? at org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)
    ??? at org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)
    ??? ...

    增加行為

    ?????? 讓我們開始第二個測試。如果documentclassUnderTest增加,我們期待調用
    mock.documentAdded()Mock對象使用document的標題作為參數:
    ?
    ?public void testAddDocument() {
    ?????? ?mock.documentAdded("New Document"); // 2
    ??????? replay(mock); // 3
    ??????? classUnderTest.addDocument("New Document", new byte[0]); 
    ????}
    如果classUnderTest.addDocument("New Document", new byte[0])調用期待的方法,使用錯誤的參數,Mock對象會拋出AssertionError:
    ?
    java.lang.AssertionError: 
    ??Unexpected method call documentAdded("Wrong title"):
    ??? documentAdded("New Document"): expected: 1, actual: 0
    ??? at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
    ??? at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
    ??? at $Proxy0.documentAdded(Unknown Source)
    ??? at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)
    ??? at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)
    ??? at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
    ??? ...

    同樣,如果調用多次此方法,則也會拋出例外:

    ?
    java.lang.AssertionError: 
    ??Unexpected method call documentAdded("New Document"):
    ??? documentAdded("New Document"): expected: 1, actual: 1 (+1)
    ??? at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
    ??? at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
    ??? at $Proxy0.documentAdded(Unknown Source)
    ??? at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)
    ??? at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)
    ??? at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
    ??? ...

    驗證行為

    ?????? 當我們指定行為后,我們將驗證實際發生的。當前的測試將會判斷是否Mock對象會真實調用。可以調用verify(mock)來山正是否指定的行為被調用。

    ?
    public void testAddDocument() {
    ??????? mock.documentAdded("New Document"); // 2 
    ????????replay(mock); // 3
    ??????? classUnderTest.addDocument("New Document", new byte[0]);
    ?????? ?verify(mock);
    ??? }

    如果失敗,則拋出AssertionError

    期待明顯數量的調用

    到現在,我們的測試只是調用一個簡單的方法。下一個測試將會檢測是否已經存在document導致mock.documentChanged()調用。為了確認,調用三次

    ?
    public void testAddAndChangeDocument() {
    ??????? mock.documentAdded("Document");
    ??????? mock.documentChanged("Document");
    ??????? mock.documentChanged("Document");
    ??????? mock.documentChanged("Document");
    ??????? replay(mock);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? verify(mock);
    ??? }

    為了避免重復的mock.documentChanged("Document"),EasyMock提供一個快捷方式。可以通過調用方法expectLastCall().times(int times)來指定最后一次調用的次數。

    ?
    ?public void testAddAndChangeDocument() {
    ??????? mock.documentAdded("Document");
    ??????? mock.documentChanged("Document");
    ??????? expectLastCall().times(3);
    ??????? replay(mock);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? verify(mock);
    ??? }

    指定返回值

    ?????? 對于指定返回值,我們通過封裝expect(T value)返回的對象并且指定返回的值,使用方法andReturn(Object returnValue)expect(T value).返回的對象。

    例如:

    ?
    public void testVoteForRemoval() {
    ??????? mock.documentAdded("Document");?? // expect document addition
    ??????? // expect to be asked to vote for document removal, and vote for it
    ??????? expect(mock.voteForRemoval("Document")).andReturn((byte) 42);
    ??????? mock.documentRemoved("Document"); // expect document removal
    ??????? replay(mock);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? assertTrue(classUnderTest.removeDocument("Document"));
    ??????? verify(mock);
    ??? } 
    ?
    ??? public void testVoteAgainstRemoval() {
    ??????? mock.documentAdded("Document");?? // expect document addition
    ??????? // expect to be asked to vote for document removal, and vote against it
    ??????? expect(mock.voteForRemoval("Document")).andReturn((byte) -42);
    ??????? replay(mock);
    ??????? classUnderTest.addDocument("Document", new byte[0]);
    ??????? assertFalse(classUnderTest.removeDocument("Document"));
    ? ??????verify(mock);
    ??? }
    取代expect(T value)調用,可以通過expectLastCall().來代替
    ?expect(mock.voteForRemoval("Document")).andReturn((byte) 42);

    等同于

    ?
    mock.voteForRemoval("Document");
    expectLastCall().andReturn((byte) 42);

    處理例外

    對于指定的例外(更確切的:Throwables)被拋出,由expectLastCall()expect(T value)返回的對象,提供了方法andThrow(Throwable throwable)。方法不得不被調用記錄狀態,在調用Mock對象后,對于此指定了要拋出的Throwable


    基本的方法,已經說完了,當然這不能完全說明EasyMock的使用。更多的因素請參考EasyMock的文檔
    http://www.easymock.org/Documentation.html
    posted on 2006-09-20 20:38 布衣郎 閱讀(3077) 評論(1)  編輯  收藏 所屬分類: 單元測試

    FeedBack:
    # re: EasyMock 2 使用指南
    2006-09-21 16:14 | 123bingbing
    如果你是編程高手,這里將是你一個展現自我的新舞臺----www.mylinux.com.cn  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    <2006年9月>
    272829303112
    3456789
    10111213141516
    17181920212223
    24252627282930
    1234567

    常用鏈接

    留言簿(12)

    隨筆分類(59)

    隨筆檔案(57)

    blog

    java

    uml

    搜索

    •  

    積分與排名

    • 積分 - 357266
    • 排名 - 155

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲aⅴ无码专区在线观看春色| 伊人久久亚洲综合影院首页| 国产免费内射又粗又爽密桃视频| 国产成人涩涩涩视频在线观看免费| 国产精品亚洲精品观看不卡| 免费在线看v网址| 亚洲综合校园春色| 特级做A爰片毛片免费69 | 亚洲国产精品无码久久SM| 亚洲五月午夜免费在线视频| 中文字幕亚洲一区二区va在线| 九九99热免费最新版| 亚洲午夜精品一区二区| 亚洲免费观看网站| 亚洲熟女综合色一区二区三区| 日韩高清免费在线观看| 黄页网址大全免费观看12网站| 亚洲综合色在线观看亚洲| 中国一级特黄的片子免费 | 无码视频免费一区二三区| 亚洲av日韩av永久在线观看| 亚洲国产精品成人一区| 久久久精品视频免费观看 | 久久综合给合久久国产免费| 亚洲综合久久成人69| 精品国产免费一区二区| 国产福利免费视频| 亚洲综合视频在线观看| 日本大片在线看黄a∨免费| 国产精品99爱免费视频| 亚洲视屏在线观看| 国产jizzjizz视频免费看| 另类免费视频一区二区在线观看| 亚洲成aⅴ人片在线观| 四虎影视在线永久免费观看| a级在线免费观看| 亚洲熟妇丰满xxxxx| 亚洲国产精品无码中文字| 黄a大片av永久免费| 日韩中文字幕免费视频| 精品久久久久亚洲|