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

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

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

    wiflish
    Loving Life! Loving Coding!
    posts - 98,comments - 98,trackbacks - 0

    使用JMock來實現孤立測試(轉)
    ?

    我們在測試某類時,由于它要與其他類發生聯系,因此往往在測試此類的代碼中也將與之聯系的類也一起測試了。這種測試,將使被測試的類直接依賴于其他 類,一旦其他類發生改變,被測試類也隨之被迫改變。更重要的是,這些其他類可能尚未經過測試,因此必須先測試這些類,才能測試被測試類。這種情況下,測試 驅動開發成為空談。而如果其他類中也引用了被測試類,我們到底先測試哪一個類?因此,在測試中,如果我們能將被測試類孤立起來,使其完全不依賴于其他類的 具體實現,這樣,我們就能做到測試先行,先測試哪個類,就先實現哪個類,而不管與之聯系的類是否已經實現。

    虛擬對象(mock object)就是為此需要而誕生的。它通過JDK中的反射機制,在運行時動態地創建虛擬對象。在測試代碼中,我們可以驗證這些虛擬對象是否被正確地調用 了,也可以在明確的情況下,讓其返回特定的假想值。而一旦有了這些虛擬對象提供的服務,被測試類就可以將虛擬對象作為其他與之聯系的真實對象的替身,從而 輕松地搭建起一個很完美的測試環境。

    JMock是幫助創建mock對象的工具,它基于Java開發,在Java測試與開發環境中有不可比擬的優勢,更重要的是,它大大簡化了虛擬對象的使用。

    本文中,通過一個簡單的測試用例來說明JMock如何幫助我們實現這種孤立測試。有三個主要的類,User,UserDAO,及 UserService。本文中,我們只需測試UserService,準備虛擬UserDAO。對于User,由于本身僅是一個過于簡單的POJO,可 以不用測試。但如果你是一個完美主義者,也可以使用JMock的虛擬它。在這領域,JMock幾乎無所不能。:)

    User是一個POJO,用以在視圖中傳輸數據及映射數據庫。其代碼如下:

    package com.sarkuya.model;

    public class User {
    ??? private String name;

    ??? public User() {
    ??? }
    ???
    ??? public User(String name) {
    ??????? this.name = name;
    ??? }
    ???
    ??? public String getName() {
    ??????? return name;
    ??? }
    ???
    ??? public void setName(String name) {
    ??????? this.name = name;
    ??? }
    }


    UserDAO負責與數據庫打交道,通過數據庫保存、獲取User的信息。盡管我們可以不用知道JMock如何通過JDK的反射機制來實現孤立測試,但至 少應知道,JDK的反射機制要求這些在運行時創建的動態類必須定義接口。在使用JMock的環境中,由于我們要虛擬UserDAO,意味著UserDAO 必須定義接口。代碼如下:

    package com.sarkuya.dao;

    import com.sarkuya.model.User;

    public interface UserDAO {
    ??? public void saveUser(User user);
    ??? public User getUser(Long id);
    }

    UserService存有UserDAO的引用,通過其對外提供應用級的服務。相應地,我們先定義了其接口(盡管在本文中,作為被測試類,UserService不需要有接口,但如果以后此類需要被虛擬,也應該帶有接口,基于此原因,我們也為其定義了接口)。

    package com.sarkuya.service;

    import com.sarkuya.dao.UserDAO;
    import com.sarkuya.model.User;

    public interface UserService {
    ??? public void setUserDAO(UserDAO userDAO);
    ???
    ??? public void saveUser(User user);
    ??? public User getUser(Long id);
    }

    可以看到,除了setUserDAO()外,其另外的方法與UserDAO一樣。這是設計模式中門面模式的典型應用,應用只通過UserService提供服務,而UserService在內部通過調用UserDAO來實現相應的功能。

    根據測試先行的原則,你應該先寫測試,再編寫實現。這里先編寫實現的原因,主要是使讀者更加清楚我們接著要測試什么。由于本文是著重介紹JMock的使用,加上UserServiceImpl比較簡單,因此先列出其代碼如下:

    package com.sarkuya.service.impl;

    import com.sarkuya.dao.UserDAO;
    import com.sarkuya.model.User;
    import com.sarkuya.service.UserService;

    public class UserServiceImpl implements UserService {
    ??? private UserDAO userDAO;
    ???
    ??? public UserServiceImpl() {
    ??? }

    ??? public void setUserDAO(UserDAO userDAO) {
    ??????? this.userDAO = userDAO;
    ??? }

    ??? public User getUser(Long id) {
    ??????? return userDAO.getUser(id);
    ??? }

    ??? public void saveUser(User user) {
    ??????? userDAO.saveUser(user);
    ??? }
    }

    下面是UserService的測試代碼:

    package com.sarkuya.service;

    import com.sarkuya.dao.UserDAO;
    import com.sarkuya.model.User;
    import com.sarkuya.service.impl.UserServiceImpl;
    import junit.framework.*;
    import org.jmock.Mock;
    import org.jmock.MockObjectTestCase;

    public class UserServiceTest extends MockObjectTestCase {
    ??? private UserService userService = new UserServiceImpl();
    ???
    ??? private Mock userDAO = null;
    ???
    ??? public UserServiceTest(String testName) {
    ??????? super(testName);
    ??? }
    ???
    ??? protected void setUp() throws Exception {
    ??????? userDAO = new Mock(UserDAO.class);
    ??????? userService.setUserDAO((UserDAO)userDAO.proxy());
    ??? }
    ???
    ??? protected void tearDown() throws Exception {
    ??? }
    ???
    ??? public static Test suite() {
    ??????? TestSuite suite = new TestSuite(UserServiceTest.class);
    ???????
    ??????? return suite;
    ??? }
    ???
    ??? public void testGetUser() {
    ??????? User fakeUser = new User("John");
    ??????? userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
    ???????
    ??????? User user = userService.getUser(1L);
    ??????? assertNotNull(user);
    ??????? assertEquals("John", user.getName());
    ??? }
    ???
    ??? public void testSaveUser() {
    ??????? User fakeUser = new User("John");
    ???????
    ??????? userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(fakeUser));
    ??????? User user = userService.getUser(1L);
    ??????? assertEquals("John", user.getName());
    ???????
    ??????? userDAO.expects(once()).method("saveUser").with(same(fakeUser));
    ??????? user.setName("Mike");
    ??????? userService.saveUser(user);
    ???????
    ??????? userDAO.expects(once()).method("getUser").with(eq(1L)).will(returnValue(user));
    ??????? User modifiedUser = userService.getUser(1L);
    ??????? assertEquals("Mike", user.getName());
    ??? }
    }

    此段代碼有幾點應注意:

    1、此測試類繼承了JMock的MockObjectTestCase

    2、private Mock userDAO = null;說明userDao是一個準備虛擬的對象

    3、在setup()中,將userDAO.class傳入Mock()后,再通過proxy()方法返回一個UserDAO的代理類實例(即虛擬對象實例),并賦值于userService

    4、在testGetUser()方法中,如果我們先將第一行及第二行代碼屏蔽掉,可以看出,這是一個真實環境下的測試代碼。先獲取一個User, 然后確認其非空值,再確認其姓名為“John”。此時,在真實環境下,這段代碼要測試成功的前提必須是UserDAO已經連接到了數據庫,然后返回一個 User后傳給UserService。
    但問題是,到目前為止,且不說UserDAO還未經歷連接數據庫這一系列繁瑣而痛苦的過程,我們甚至還未實現UserDAO的接口!那么,為何加上第一行 及第二行代碼后就可以了呢?這正是JMock的威力所在。先實例化一個測試用的fakeUser,然后通過一系列的指令,在第二行代碼中告訴JMock應 該如何“做假”。盡管這句代碼很長,我們可作如下理解:
    1) userDAO.expects(once()):我們期望userDAO的某方法被執行一次,如果此方法未被執行,或者執行了二次以上,測試就不會通過
    2) method("getUser"):這個期望被執行一次的方法名為userDAO.getUser()
    3) with(eq(1L)):執行getUser()方法時,確認其傳入的參數值為“1L”
    4) will(returnValue(fakeUser)):上述條件均滿足后,返回一個虛假的對象,即我們前面實例化的fakeUser
    總體來說,當設定好第二行語句后,JMock就在后臺監控著,確保userDAO.getUser()必須,且只被執行一次,且參數“1L”已經正確地傳給了此方法,一旦這些條件被滿足,就返回fakeUser。
    而在第三行,User user = userService.getUser(1L)將觸發所有這些條件,作為獎勵,它接受了獎品fakeUser并賦值于user對象。而下面第四行及第五行均對此user對象進行測試,不通過才怪。

    5) testSaveUser()方法中的原理類似。其思路是,將id為“1”的user從數據庫中取出,將其名改為“Mike”,再存回數據庫,然后再從數據庫中取出此user,確保其名字已被改變。
    第五行userDAO.expects(once()).method("saveUser").with(same(fakeUser))比較特殊。首 先,with(same(fakeUser))說明,傳入參數必須是fakeUser此實例,盡管我們在下面的語句中通過user.setName ("Mike"),但只是改變了其name的屬性,而fakeUser的實例引用并未發生改變,因此可以滿足條件。其次,其后沒有.will (returnValue(fakeUser)),因為userDAO.saveUser()不需要返回任何對象或基本數據類型。
    另外,當再次執行userDAO.expects()時,JMock將重設其監控條件。我們也可以通過userDAO.reset()來顯式是清除監控條件。

    通過以上實例代碼及其說明,我們看出,用好JMock的關鍵是先設置監控條件,再寫相應的測試語句。一旦設好監控條件后,在某段代碼塊執行完畢時, 如果監控條件未得到滿足,或是沒有通過expects()再次重設條件,或通過reset()來顯式是清除監控條件,測試將無法通過。

    以上介紹了JMock的基本使用方法。而這種基本用法,占了全面掌握JMock所需學習的知識70%以上。關于JMock的更多細節,感興趣的讀者可以訪問JMock的網站進一步學習。

    posted on 2007-04-10 14:40 想飛的魚 閱讀(591) 評論(0)  編輯  收藏 所屬分類: java
    主站蜘蛛池模板: 亚洲香蕉成人AV网站在线观看| 在线观看视频免费完整版| 国产成人3p视频免费观看| 亚洲欧洲AV无码专区| 久久久www成人免费毛片 | 免费视频成人手机在线观看网址| 久久精品国产亚洲7777| 久久毛片免费看一区二区三区| 亚洲伊人久久大香线蕉综合图片 | 久久99国产综合精品免费| 亚洲成人在线网站| 免费能直接在线观看黄的视频| 亚洲福利视频网址| 欧洲精品成人免费视频在线观看 | 亚洲国产成人精品久久久国产成人一区二区三区综 | 四虎影视免费在线| 亚洲精品无码少妇30P| 国产小视频在线观看免费| a毛片成人免费全部播放| 亚洲精品乱码久久久久久按摩| 免费无码中文字幕A级毛片| 亚洲妇女无套内射精| 国产在线98福利播放视频免费| 一个人免费观看www视频| 亚洲av色影在线| 欧美日韩国产免费一区二区三区| 亚洲GV天堂GV无码男同| 久久精品国产亚洲Aⅴ蜜臀色欲| 日韩免费视频一区二区| 亚洲日本久久久午夜精品| 免费一级毛片不卡在线播放| 久久国产乱子伦精品免费强| 亚洲天堂免费在线| 91成人免费观看| 亚洲精品自偷自拍无码| 亚洲午夜日韩高清一区| 最近最新高清免费中文字幕 | 美女露隐私全部免费直播| 亚洲国产一二三精品无码| 97碰公开在线观看免费视频| 国产成人va亚洲电影|