顯示具有 JUnit 標籤的文章。 顯示所有文章
顯示具有 JUnit 標籤的文章。 顯示所有文章

2009年4月4日 星期六

[JUnit] 如何測試一個 private method

上次幫老師帶了一堂課後,那天同學有反應說關於測試的問題:要如何測試 private method 呢?這個問題好像有點難,不過如果我們利用 Java 提供的 reflection 機制就可以輕鬆的辦到!不過那也要會用才行囉!所以我就示範一下如何撰寫這樣的程式。
假設我們有一個程式如下:
public class HavePrivateMethod
{
private int add(int x, int y)
{
return x + y;
}
}

這個程式很簡單,可能你連用都不想用XD,不過因為是示範就別太計較了!反正就是有個 private method 為 add,parameter 與 return type 都是 int。以下就是我們的測試程式:

public class HavePrivateMethodTest
{
@Test
public void add() throws Exception
{
int x = 1;
int y = 2;
int expectedResult = 3;
/*
* 1.取得 instance
*/
HavePrivateMethod havePrivateMethodClass =
HavePrivateMethod.class.newInstance();
/*
* 2.取得我們要測試的 method
*/
Method addMethod =
havePrivateMethodClass.getClass().getDeclaredMethod(
"add",int.class,int.class);
/*
* 3.設定存取性
*/
addMethod.setAccessible(true);
/*
* 4.實際呼叫
*/
Object actualResult =
addMethod.invoke(havePrivateMethodClass, x, y);
addMethod.setAccessible(false);
Assert.assertEquals(expectedResult, actualResult);
}
}

總之,我們要先取得一個該 class 的 instance,再來就是設定我們要測試的 private method,要取得此 method 的 Method instance,由於我們測試的 private method 有兩個 primitive type 的 int,所以我們就用 int.class,而不是用 Integer.class 喔!因為這不一樣的~
取得 Method instance 後,我們就要先設定讓此 method 是可以被存取的,接下來才是呼叫(invoke) 此 method,呼叫後的回傳值一律都是 Object 型態,我們就透過 JUnit 提供的 assertEquals() method 來幫助我們檢查!
這樣就完成我們的目標囉~

NOTE:(2009-05-06)
在上面的範例程式中,我們採用 HavePrivateMethod.class.newInstance(); 來初始化被測試的物件,不過這樣的初始化動作只限定用在被測的 class 具備有 default constructor!如果我們的被測試 class 沒有 default constructor 的話,我們就只能用一般的初始化方式,也就是 new Object 囉~這樣在 method reflection 也是會有效的!
感謝學弟大頭跟 Fred 提出這樣的問題~

2008年7月29日 星期二

[Struts] StrutsTestCase for JUnit

Introduction

對於寫過test case 的 programmer 來說,JUnit 應該是不陌生,JUnit 是被大家公認用來執行java測試最好的framework,但是對於像Struts這種以MVC為宗旨的framework,我們又該如何去執行測試呢?StrutsTestCase for JUnit[1] 就是一個用來達成我們目的的solution。從字面上就可以了解,StrutsTestCase其實就是將JUnit延伸到Struts framework可以使用的library。所以,大致上對於測試的方式是大同小異的,不過對於StrutsTestCase[1]來說,還是有一些不一樣之處。

下圖一是官方網站的簡介,此網站對於StrutsTestCase[1]有很詳細的說明。

clip_image003

圖一 官方網站的說明

圖一中的紅圈處就可以下載該library。點選後就會進入SourceForge.net的網站,如下圖二。

clip_image005

圖二 SourceForge.net的下載點

接個點選Download字樣,進入如下圖三的畫面。

clip_image007

圖三 SourceForge.net的下載點(2)

畫面中請下載您所需要的版本,通常是選擇下載最新版本為主,圖中最後一個載點是在檔名後方加上 -src代表是原始碼的版本,若您有需要訂做自己適用的StrutsTestCase,您也可以下載原始碼版本。

下載完的壓縮檔案解開後的資料夾中會包含一個strutstest-x.jar(其中x會依照您所下載的版本不同而有所改變)。若是一般的 Java Application 要使用,請設定 classpath 位置;若是需要在 Web Server 上面的 Web Application 使用,請放置該 Web Application 的 WEB-INF / lib 之下。

Example

首先假設您有一個action如下:

public class LoginAction extends Action {

    public ActionForward excute(...) {

        String username = ((LoginForm)form).getUsername();

        String password = ((LoginForm)form).getPassword();

        String role = “user”;

        if (username.equals(“adm”) && password.equals(“test”)) {

            role = “adm”;

        }

        request.getSession().setAttribute(“role”,role);

        request.getSession().setAttribute(“username”,username);

    }

}

上述程式碼中,我們假設您已經有一個LoginForm class。上述的action您在struts-config.xml中設定如下:

<action path=”/login” type=”LoginAction” />

接下來您就可以根據您的action來開發strutstestcases測試,開發strutstestcase步驟如下:

(1) 您必須建立新的class,我們命名為LoginActionTest並且extends MockStrutsTestCase:

public class LoginActionTest extends MockStrutsTestCase {

}

(2) 如果您是在 eclipse 環境下執行測試,請您務必加入以下的程式碼:

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”)); //註一

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

}

註一:其中WebContent要根據您在eclipse中建立的資料夾而定,依照我所設定的資料夾如下圖四,所以我們採用WebContent:

clip_image013

圖四 Eclipse專案的目錄結構

(3) 接著我們就要開始來正式的撰寫我們的test case,所有的test case method都必須以test開頭,例如:testLogin。

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”));

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

    public void testLogin() {}

}

(4) 有了test case method,我們就要開始撰寫內容了。首先,我們要先告知strutstestcase我們要測試的路徑,也就是瀏覽器如何存取該action的方法,不過,我們只需要設定在struts-config.xml中的路徑即可,無須增加IP位址,因為MockStrutsTestCase是屬於Mock Test測試(也就是非in-container測試,所謂的container就是由tomcat這類的server來啟動,所以in-container就是會啟動tomcat server來進行測試,Mock Test就是另一種不用啟動tomcat server的離線測試),先前我們已經將LoginAction class在struts-config.xml中設定的路徑為/login,所以我們可以透過MockStrutsTestCase所提供的setRequestPathInfo() method來設定action的存取位置。

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”));

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

    public void testLogin() {

        super.setRequestPathInfo(“/login”);

    }

}

NOTE: 呼叫superclass的methods時,可以使用super關鍵字,本人建議所有呼叫superclass的methods時,都加上super關鍵字,以方便未來觀看程式碼時就可以一目了然,另外,若是呼叫同一個method時,請您也加上this來方便日後的維護。這並沒有硬性的規定,所以的關鍵字都不加上去,程式碼依舊可以執行,這只是習慣性的問題而已。

(5) 再來就是告知該action我們要傳入的資料,此資料就是模擬使用者會傳入哪些資料給action。切記,雖然我們的action是接收一個ActionForm物件,不過在MockStrutsTestCase中是用類似URL GET method(../login.do?username=adm&…)的方式給予。我們並不能給予ActionForm物件。所以我們要呼叫MockStrutsTestCase的addRequestParameter() method,並將我們要給予的參數名稱與參數值。

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”));

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

    public void testLogin() {

        super.setRequestPathInfo(“/login”);

        super.addRequestParameter(“username”,”adm”);

        super.addRequestParameter(“password”,”test”);

    }

}

(6) 設定好路徑與參數後,我們就要告知MockStrutsTestCase,該測試可以啟動了,所以我們就呼叫他的actionPerform() method來正式的執行該action。

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”));

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

    public void testLogin() {

        super.setRequestPathInfo(“/login”);

        super.addRequestParameter(“username”,”adm”);

        super.addRequestParameter(“password”,”test”);

        super.actionPerform();

    }

}

(7) Action執行完後,我們就可以針對action執行後的結果作測試,因為我們的action是將結果儲存在session中,所以我們就要從session中取出結果,並且呼叫JUnit的測試method來檢驗結果。

public class LoginActionTest extends MockStrutsTestCase {

    public void setUp() {

        super.setUp();

        setContextDirectory(new File(“WebContent”));

        this.setConfigFile(“/WEB-INF/struts-config.xml”);

    }

    public void testLogin() {

        super.setRequestPathInfo(“/login”);

        super.addRequestParameter(“username”,”adm”);

        super.addRequestParameter(“password”,”test”);

        super.actionPerform();

        String role = super.getSession().getAttribute(“role”);

        super.assertEquals(role,”adm”);

    }

}

NOTE:若action是將結果儲存在request中,您就要改呼叫super.getRequest() method來進行測試。

References

[1] StrutsTestCase官方網站 http://strutstestcase.sourceforge.net/

2008年3月11日 星期二

[Java] JUnit 3.8.1 : An Unit Test Framework for Java

Introduction

對於 programmers 來說,如何確保自己的程式碼是正確的,通常都必須要自己親自去執行程式碼,然後自己扮演使用者的角色來手動測試,這已經是早期的 programmers 會做的是了。JUnit 就是一套 framework,他幫助 programmers 解決測試中繁雜的工作,透過撰寫 unit test code 來達到測試的效果,並且採用自動化、可重複測試的功能來降低 programmers 的工作。

clip_image002

圖一 JUnit 官方網頁

在 Eclipse 中,JUnit 是核新的 plug-in,除非您將他反安裝了,否則您可以在 Eclipse 中找到。

Example

要撰寫一個 unit test code 其實是一件很簡單的事,因為 JUnit library 都已經將繁雜的工作改以簡短的程式碼來解決。

(1) 首先,我們要先建立一個 JUnit 的 class,點選 eclipse 中的 [File] -> [New] -> [Others],接著您就會看到如圖二的畫面出現。

clip_image004

圖二 建立新的元件視窗

(2) 點選該視窗的 [JUnit Test Case],並且會出現如圖三的畫面。其中我們選擇上方的 [New JUnit 3 test],並且我們將我們的 unit test case class name 填入 name 欄位,接著選擇下方的 [Class under test] 右方的 [Browse…] 按鈕,接著會出現視窗讓我們選擇我們想要測試的 class。

clip_image006

圖三 建立新的 JUnit Test Case

(3) 選擇好上述的資訊並填寫完畢後,按下 [Next>] 後會出現如圖四的視窗。我們可以由此視窗中選擇我們對於該 class 中我們想要測試的 methods,並將他打勾,eclipse 會自動幫我們產生測試的基本程式框架,以方便我們測試。按下 [Finish] 後就會開啟測是的程式碼,我們會看到剛才我們所勾選的 methods 會以 testXXX() 的方式出現。

NOTE:本文件示範的 JUnit 版本為 JUnit 3,若您有興趣可以自行研究 JUnit 4,不過要先對於 JDK 5.0 的 Annotation 有基本的概念才行。

clip_image008

圖四 選擇目標 class 我們想要測試的 methods

(4) 有了程式的框架,我們當然還是要自己撰寫測試的 scenarios。首先我們從新假設我們要測試的目標 class 內容如下:

public class Math {

    public int add(int x, int y) { return x + y; }

    public int subtract(int x, int y) { return x - y; }

}

(5) 接著我們就用上述的 eclipse 工具幫我們產生測試程式碼的框架:

public class MathTest extends TestCase {

    public void setUp() { super.setUp(); }

    public void testAdd() {}

    public void testSubtract() {}

}

上述程式碼中的 setup() method 是用於起始設定的 method,也就是 JUnit 在呼叫每個測試 methods 前都會先執行 setup() method,如上面的測試程式碼中,JUnit 會依照下面的順序執行測試:setUp(), testAdd(), setUp(), testSubtract()。如果您的測試中是需要先建利一些物件,如:資料庫的連線 class等,您就不必在每個測試 methods 中撰寫建立這些物件的程式碼,您可以統一撰寫在 setup() method 中。

您所有的測試 methods 都必須以 test作為開頭,因為 JUnit 測試會根據 method 前面為 test 開頭的來進行測試。

(6) 接著我們就要開始撰寫測試的 scenario,在此我們就只示範 testAdd() method。

public class MathTest extends TestCase {

    public void setUp() { super.setUp(); }

    public void testAdd() {

        int x = 10;

        int y = 50;

        int expected = 60;

        int result = this.math.add(x, y);

        super.assertEquals(result, expected);

    }

}

上述程式碼中,我們先宣告一個class內部變數 math他是我們測試的目標,並且我們在 setup() method 中將他初始化。接著假設我們要輸入的 x and y 的值,以及我們所期望的輸出結果 expected 變數,然後我們就呼叫 Math 的 add() method 來獲得結果並存在 result 變數中,最後我們就呼叫 super class 中的 assertEquals 來斷定結果一定是要相同。

(7) 再來就是執行我們的測試案例,選擇 [Run] -> [Run As] -> [JUnit Test]。測試結果會如圖五。

clip_image013

圖五 測試結果報告視窗

如果您的測試無誤的話,就會在右邊出現綠色的 bar,左邊上方有一些測試的結果資訊,紀錄著您總共測試的 methods、測試中出現的 Errors 與測試中測試與您結果不符合的 Failures。

References

[1] JUnit 官方網站 http://junit.sourceforge.net/junit3.8.1/index.html

[2] Eclipse 官方網站 http://www.eclipse.org