Junit&Jmock使用簡介


Junit&Jmock簡介
序言 2
1. 環境配置 2
2.一種比較好的命名方式 3
3. JUnit使用入門 4
3.1一種簡單的實現 4
3.2添加初始化和銷毀方法的實現 5
3.3對Java異常(Exception)的單元測試 8
3.4 Assert類 9
3.5小結 12
4.Jmock使用入門 12
4.1一種簡單的實現 13
4.2 Expectations類 18
4.3小結 21
5.總結 22




 
序言
本文的寫作目的旨在幫助大家用最少的時間學會使用Junit和Jmock,並能夠使用它們對Java程序進行單元測試。並不拘禮它們的實現原理,所以下面從最佳實踐的角度,介紹一種使用Junit和Jmock常用的方式。


Junit從4.0版本開始做了較大的改動,根據Java5.0的新特性(注解、靜態導入等)構建而成。Junit最新版本應該是大於等於4.9的。本文 選擇相對成熟穩定的版本4.8.2。JUnit4提供了3種機制進行單元測試,大家所熟悉的斷言方式,以及新的假設機制和理論機制。假設機制和斷言的使用 方式差不多,唯一不同的是假設機制出錯時並不中斷測試用例的執行,理論機制時對那些需要無窮個測試用例才能正確描述的代碼行為的概括性陳述。由於假設機制 和理論機制使用得並不多,本文僅針對斷言機制展開介紹。


Jmock當前最新的穩定版本為2.5.1,本文選擇該該版本對Jmock 的基本使用情況進行介紹。可以把JMock當做JUnit測試的一個很好的補充,因為有的情況,僅僅使用JUnit很難完成測試,這樣的情況有真實對象具 有不可確定的行為,產生不可預測的效果;真實對象很難被創建的;真實對象的某些行為很難被觸發;真實對象實際上還不存在的(和其他開發小組或者和新的硬件 打交道) 等等。結合JMock對Java源代碼進行JUnit測試,能夠基本完成所有單元測試的需求。Jmock是一個利用Mock對象來測試Java代碼的輕量 級工具。所謂Mock對象,就是用來模擬真實對象,通過真實對象的行為與被測試對象進行交互,從而實現對被測對象進行測試的目的。一般在被測對象需要與其他真實對象進行交互或者依賴於其他真實對象時需要用到Mock對象。


本文的安排是這樣的,第1章介紹Junit和Jmock環境的配置。第2章介紹Junit使用的最佳實踐,首先介紹了一下命名方式,接着介紹一種簡單的 Junit使用方式,這種方式幾乎能夠滿足大部分需求,2.3節在2.2節使用的基礎上,添加初始化和銷毀方法的實現。2.4節簡要介紹了對拋出異常進行 單元測試的方法。這幾種方式幾乎能夠滿足所有使用Junit進行測試的需求。本文最后介紹了Assert類的詳細信息。
1. 環境配置
1.1 環境准備
1. Eclipse(版本無特殊規定)
2. Junit4.8.2(版本 >= 4.0,本文的測試版本為4.8.2)
3. JMock2.5.1(當前最新的穩定版本)




Eclipse下載地址:http://www.eclipse.org/downloads/
junit-4.8.2.jar, 下載地址見https://github.com/KentBeck/junit/downloads
JMock2.5.1, 下載地址見http://www.jmock.org/download.html


1.2 環境配置
Junit的配置特別簡單,如下所示:
1. 選擇Eclipse中Project,單擊右鍵->Buid Path->Add External Archive…
2. 在彈出的文件選擇對話框中選擇junit-4.8.2.jar即可。


JMock的配置跟JUnit的配置一樣,只需要導入相應的jar包即可。需要導入的jar包如下所示:
Jmock-2.5.1.jar
Hamcrest-core-1.1.jar
Hamcrest-library-1.1.jar
Jmock-legacy-2.5.1.jar
Cglib-nodep-2.1_3.jar
Objenesis-1.0.jar
2.一種比較好的命名方式
在Project下建立一個源文件夾,命名為test。在test下面建立要測試Java類的Junit測試類,命名規則為在Java類后面添加 Test,表示該類為src下某個Java類的Junit測試類。顯然,test下面這個類的package路徑和src下對應類的package路徑是 一樣的。這樣一方面保證測試用例與實際項目分離,另一方面也方便了對測試用例的管理。命名結果如下圖所示。
 
針對src中的類,在test中對應的類中添加相應的測試方法,方法基本格 式為:public void testXxx() {}, 其中方法名並不要求必須是以test開頭的形式,但以test開頭是很好的使用習慣,能夠一目了然的發現這個測試用例是測試的哪個方法。並且在Junit 早期的版本中要求必須使用testXxx的形式為測試方法命名,為了兼容性,也應該這樣命名。添加了測試方法后的效果圖如下所示。
 
這種方式能夠通過測試方法一目了然的知道該方法是測試的哪一個業務邏輯類中的哪一個方法。這種方式還有一個優點是可以通過腳本對test目錄下的所有類進行自動化測試,當測試用例過多時是很好的一種方式。
3. JUnit使用入門
3.1一種簡單的實現


1. 編寫Logic1.java類的內容,如下所示:

public class Logic1 {

public int abs(int n) {
return n >= 0 ? n : (-n);
}


}


2. 編寫Logic1Test.java類的內容,如下所示:


import org.junit.Assert;
import org.junit.Test;


public class Logic1Test {    //注意,並不需要繼承其它類

@Test      //注意,Test注解為必須
public void testAbs() { 
Logic1 logic1 = new Logic1();
int i = -15;

Assert.assertEquals(i, logic1.abs(i));
}
}


在Logic1Test類中單擊右鍵->Run as->Junit Test即可運行,當Assert得到錯誤時單元測試會不成功,更詳細的Assert操作將在后續介紹。


上面的方法將業務邏輯相關的類放在測試方法中進行初始化,當初始化工作較多且多個測試方法需要共用一些邏輯類時,這種方法比較低效。下面介紹一種解決上述問題的方法。
3.2添加初始化和銷毀方法的實現
添加初始化和銷毀方法可以使用下面幾個Java注解實現,它們分別是:@Before、@After、@BeforeClass和@AfterClass,其解釋如下表所示:


@Before 測試方法執行前,每一個測試方法執行前均會調用一次
@After 測試方法執行后,每一個測試方法執行后均會調用一次
@BeforeClass 測試方法執行前,每一個類僅執行一次,要求方法為static
@AfterClass 測試方法執行后,每一個類僅執行一次,要求方法為static


 @Before和@After使用案例


public class Logic2 {

public int abs(int n) {
return n >= 0 ? n : (-n);
}


}


import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;


public class Logic2Test {

Logic2 logic2;

@Before
public void init() {
System.out.println("Before Test method...");
logic2 = new Logic2();
}

@After
public void destory() {
System.out.println("After Test method...");
}

@Test
public void testAbs1() {
System.out.println("run Test method abs1...");
Assert.assertEquals(7, logic2.abs(7));
}

@Test
public void testAbs2() {
System.out.println("run Test method abs2...");
Assert.assertEquals(8, logic2.abs(-8));
}
}


運行測試用例后結果如下:
Before Test method...
run Test method abs1...
After Test method...
Before Test method...        //注意:每個方法初始化了一遍
run Test method abs2...
After Test method...




 @BeforeClass和@AfterClass使用案例

public class Logic3 {

public int abs(int n) {
return n >= 0 ? n : (-n);
}


}


import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;


public class Logic3Test {

static Logic3 logic3;         //注意static

@BeforeClass
public static void init() {         //注意static
System.out.println("Before Test method...");
logic3 = new Logic3();
}

@AfterClass
public static void destory() {            //注意static
System.out.println("After Test method...");
}

@Test
public void testAbs1() {
System.out.println("run Test method abs1...");
Assert.assertEquals(7, logic3.abs(7));
}

@Test
public void testAbs2() {
System.out.println("run Test method abs2...");
Assert.assertEquals(8, logic3.abs(-8));
}
}


運行測試用例后結果如下:
Before Test method...               //注意:僅初始化一次
run Test method abs1...
run Test method abs2...
After Test method...


這種方式保證了初始化和銷毀操作只進行一次,但要求@BeforeClass和@AfterClass所修飾的方法為static,由此需要相應的類也需要設置為static。


3.3對Java異常(Exception)的單元測試
有時需要測試業務邏輯方法是否按要求拋出了異常,Junit對這個需求也有很好的支持,通過在Test注解中添加expected選項參數指定可能拋出的異常。實際例子如下所示:


public class Logic4 {

static class SpecialException extends Exception 
{
private static final long serialVersionUID = 1L;

public SpecialException() {}

public SpecialException(String msg) {
super(msg);
}
}

public void testException(boolean isTriggerException) throws SpecialException 
{
if (isTriggerException) {
throw new SpecialException("This is a exception for junit test!");
}else {
//do nothing...
}

}


}


import org.junit.Test;


public class Logic4Test {    

@Test(expected=Logic4.SpecialException.class)      
public void testTriggerException() throws Logic4.SpecialException {
Logic4 logic4 = new Logic4();

//logic4.testException(false);    //單元測試失敗
logic4.testException(true);    //單元測試成功
}
}

需要注意的是,不能在testTriggerException方法中對可 能拋出的異常進行攔截處理,如try-catch的方法,這使得異常並沒有拋到Junit框架里面去,導致單元測試始終為失敗,這里不需要處理異常,直接 將異常繼續拋出去,Junit框架會感知這個異常,並判斷與expected參數設置的異常是否一致,從而判斷單元測試是否成功。
3.4 Assert類
Junit中提供了一個Assert類,該類封裝了大量斷言的方法,如assertEquals、assertNotNull等。Assert在線文檔見:http://www.junit.org/apidocs/org/junit/Assert.html
使用該類有兩種方式:
1. 直接使用,如:Assert.assertEquals(param1, param2);
2. 先靜態導入再使用,如:
Import static org.junit.Assert.assertEquals;
assertEquals(param1, param2);    //直接使用


下面對常用的斷言方法進行總結,以方便使用時索引,如下表所示:
assertEquals 斷言兩個參數是否相等
assertArrayEquals 斷言兩個參數數組是否相等
assertNotNull 斷言參數是否為空
assertSame 斷言兩個參數是否相等,為對象時,是否為同一對象引用
assertTrue 斷言參數是否為真
assertThat 斷言參數是否滿足某種條件,JUnit4新語法,后續會介紹
fail 斷言失敗,即測試用例測試失敗。當業務邏輯復雜以至於使用上述方法很難實現斷言測試時,可自己寫業務邏輯,在不匹配時通過fail指定斷言失敗


值得一提的是,對所有double類型的參數進行斷言時,和其他類型的斷言方式都不一樣,由於浮點數在計算機中存儲會存在一定的誤差,所以還需要一個誤差參數,表明在誤差允許的范圍之內判斷斷言兩個double類型的數據是否相等。


Assert類的使用案例如下所示:
package example;


import static org.junit.Assert.*;


import org.hamcrest.CoreMatchers;
import org.hamcrest.core.Is;
import org.junit.Test;


public class AssertUseTest {

@Test
public void testAssert() {
long i = 5, j = 5;
assertEquals(i, j);

double m = 5.5, n = 5.6, delta = 0.5;
assertEquals(m, n, delta);

int[] arr1 = {3, 5, 6};
int[] arr2 = {3, 5, 6};
assertArrayEquals(arr1, arr2);

Object o = new Object();
assertNotNull(o);

Object o1 = new Object();
assertNotSame(o, o1);

o1 = o;
assertSame(o, o1);

boolean flag = true;
assertTrue(flag);

boolean hasFailed = true;
if (hasFailed) {
//fail();            //引發斷言失敗
}

assertThat("123456", Is.is("123456"));
assertThat(1, CoreMatchers.is(1));

//假設出錯時,后續代碼不會執行,測試用例正確結束,繼續執行其他測試用例
//Assume.assumeTrue(false);   //halt
//System.out.println("halt or not?");
}
}
assertThat
assertThat是JUnit4支持的一個新的斷言方法。assertThat引入了Hamcrest測試框架,這個框架提供了一套通用的匹配符Matcher,靈活使用這些匹配符定義的規則,能夠更加精確的表達測試思想,指定所想設定的測試條件。
assertThat語法如下:
public static <T> void assumeThat(T actual, Matcher<T> matcher);
其中,actual是接下來想要測試的變量值。Matcher是使用Hamcrest匹配符來表達的對前面變量所期望值得聲明,如果actual與matcher表達式所表達的期望值相符,則測試成功,否則測試失敗。
Matcher在Hamcrest中為一個接口,文檔中顯示,已經實現該接口的類如下圖所示。
 
具體每個類的意義請參考其文檔。下面以Is為例,說明assertThat的用法如下:

assertThat("123456", Is.is("123456"));
assertThat("12345", IsNot.not("123456"));
assertThat(o, IsSame.sameInstance(o1));

assertThat(o, 
AllOf.allOf(
IsSame.sameInstance(o1), 
IsNot.not(new Object()), 
Is.is(o)));

List matchers = new ArrayList(); matchers.add(IsSame.sameInstance(new Object()));  //測試為假 matchers.add(Is.is(o1)); assertThat(o, AnyOf.anyOf(matchers)); //自定義Matcher          //其中CoreMatchers實現了上面提到類的所有表達式,可直接使用 assertThat(1, CoreMatchers.is(1)); assertThat(o, CoreMatchers.anyOf(matchers)); 如上所述,如果assertThat用得好,可以完全替代Assert類中的其他斷言方法,但對於簡單的測試用例,Assert提供的測試方法已經足夠使用。 3.5小結 本章主要還是通過實例的方式,從Junit使用的最佳實踐入手,介紹了使用Junit的方式。總的來說包括Junit使用的命名方式,這種方式能夠 通過測試方法一目了然的知道該方法是測試的哪一個業務邏輯類中的哪一個方法。通過一種簡單的Junit實現介紹了如何對Java業務邏輯方法進行單元測 試,通常情況下,這種方式已經能夠滿足大部分需求。當初始化工作較多且多個測試方法需要共用一些邏輯類時,這種方法比較低效,可以使用2.2節中介紹的 @Before、@After、@BeforeClass、@AfterClass很好的解決這個問題。2.3節簡要介紹了對拋出異常進行單元測試的方 法。 4.Jmock使用入門 在某些情況下:如有一個A類,其依賴於B類,然后B類很難被真實創建或者還根本就沒有被實現,在這種情況下,使用JUnit對其測試有些力不從心,無從下 手,而Jmock很好的補充了JUnit在這種情況下的缺陷,JMock通過模擬一個Mock對象用於取代真實的B類對象,模擬的Mock對象有着B類同 樣的接口,從而使得A類的方法能夠執行成功,這樣才能對A類進行JUnit測試。 可以把JMock當做JUnit測試的一個很好的補充,結合JMock對Java源代碼進行JUnit測試,能夠基本完成所有單元測試的需求。 下面來看Jmock如何使用,Jmock的使用也特別簡單,基本邏輯分為如下幾步: 1. 創建一個Mockery對象,如下所示: Mockery context = new Mockery(); 2. 創建Mock對象,非常簡單,如下所示: final T t = context.mock(T.class);    //這里需要final,后面在子類中會用到 3. 對Mock對象添加預定的期望,代碼如下所示: Context.checking(new Expectations() { { //Expectations的語法,需要放在一對大括號之內 oneOf(subscriber).receive(message);  will(returnValue(message); inSequence(seq); //其中期望類Expectations將在3.2節詳細介紹 } }); 4. 被測試對象的業務邏輯調用 5. 查看Mock是否滿足被測結果,代碼如下: context.assertIsSatisfied(); 4.1一種簡單的實現 下面同樣通過一個簡單的實例來說明Jmock如何使用。該實例涉及一下幾個類,如下圖所示。該實例想要測試登錄功能是否正確,但由於登錄功能 LoginAction類依賴於DatabaseConnection和DatabaseManager兩個接口。這兩個接口具體的實現與采用的數據庫相 關,並且此時並沒有實現這兩個接口,甚至還沒有決定要選擇哪一個數據庫產品呢?對於這種情況的測試,Jmock能夠很好的完成需求,下面就來看一下這幾個 類的源代碼。   public class LoginAction { DatabaseConnection databaseConnection; DatabaseManager databaseManager; public void setDatabaseConnection(DatabaseConnection databaseConnection) { this.databaseConnection = databaseConnection; } public void setDatabaseManager(DatabaseManager databaseManager) { this.databaseManager = databaseManager; } public String login(String username, String password) throws DatabaseConnException { if (! databaseConnection.isConnectioned()) { throw new DatabaseConnException("database connection error"); } boolean isExistAccount = databaseManager.isExistAccount(username, password); if (true == isExistAccount) { return "login successed"; } return "login failed"; } } DatabaseConnection和DatabaseManager接口源碼如下: public interface DatabaseConnection { public boolean isConnectioned(); } public interface DatabaseManager { public boolean isExistAccount(String username, String password); } public class DatabaseConnException extends Exception { private static final long serialVersionUID = 1L; public DatabaseConnException() {} public DatabaseConnException(String message) { super(message); } } LoginAction類的單元測試類為LoginActionTest,其源碼如下所示: import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.Sequence; import org.junit.Assert; import org.junit.Test; public class LoginActionTest{     //還記得JUnit單元測試里面的注解嗎?? @Test public void testLoginSuccessedExample() throws DatabaseConnException { Mockery context = new Mockery();         //創建兩個Mock對象,用於模擬真實對象 final DatabaseConnection databaseConnection =  context.mock(DatabaseConnection.class); final DatabaseManager databaseManager = context.mock(DatabaseManager.class); LoginAction loginAction = new LoginAction(); loginAction.setDatabaseConnection(databaseConnection); loginAction.setDatabaseManager(databaseManager); //創建了一個Expectations類的實例,下一節將詳細介紹Expectations類 context.checking(new Expectations() { {                 //對這Mock對象方法調用時的期望,如此處設置希望輸入參數為”root”和”root”時,返回true oneOf(databaseManager).isExistAccount("root", "root"); will(returnValue(true)); oneOf(databaseConnection).isConnectioned(); will(returnValue(true)); } });          String s = loginAction.login("root", "root");         //JMock測試,測試Mock對象調用方法時的期望是否滿足 context.assertIsSatisfied();         //JUnit單元測試方法,測試登錄是否成功(注意Mock對象扮演的角色) Assert.assertSame("login successed", s); } @Test public void testLoginFailedExample() throws DatabaseConnException { Mockery context = new Mockery(); final DatabaseConnection databaseConnection = context.mock(DatabaseConnection.class); final DatabaseManager databaseManager = context.mock(DatabaseManager.class); LoginAction loginAction = new LoginAction(); loginAction.setDatabaseConnection(databaseConnection); loginAction.setDatabaseManager(databaseManager);         //注意順序的設置,跟login方法中兩個方法的調用順序有要求 final Sequence seq = context.sequence("loginActionTest"); context.checking(new Expectations() { {                 //此處期望先調用isConnectioned,然后isExistAccount方法                 //看看login方法中是否也是這樣的呢,不是的話,測試就會失敗 oneOf(databaseConnection).isConnectioned(); will(returnValue(true)); inSequence(seq); oneOf(databaseManager).isExistAccount("root", "root"); will(returnValue(false)); inSequence(seq); } }); String s = loginAction.login("root", "root"); context.assertIsSatisfied(); Assert.assertSame("login failed", s); } //測試拋出異常的情況 @Test(expected=DatabaseConnException.class) public void testLoginThrowsDatabaseConnException() throws DatabaseConnException { Mockery context = new Mockery(); final DatabaseConnection databaseConnection = context.mock(DatabaseConnection.class); final DatabaseManager databaseManager = context.mock(DatabaseManager.class); LoginAction loginAction = new LoginAction(); loginAction.setDatabaseConnection(databaseConnection); loginAction.setDatabaseManager(databaseManager); context.checking(new Expectations() { { oneOf(databaseConnection).isConnectioned(); will(returnValue(false)); } }); loginAction.login("root", "root"); context.assertIsSatisfied(); } } 對上述代碼的解釋,以上對LoginAction類的login方法進行了3個單元測試,這3個單元測試方法能夠正常結束。 第一個單元測試方法為testLoginSuccessedExample方法,由於Mock對象模擬了兩個,方法均返回true,所以login方法將 返回字符串s,注意最后一行Assert類對這個login方法返回的字符串與“login successed”進行的比較,斷言這兩個字符串相同,如果不同的話,單元測試方法將失敗。 第二個單元測試方法為testLoginFailedExample,Mock對象期望isExistAccount方法返回false,此時login方法調用返回的字符串s與”login failed”比較,相同則單元測試通過。 第三個單元測試方法為 testLoginThrowsDatabaseConnException,用於測試login方法拋出DatabaseConnException異 常,只要Mock對象期望isConnectioned返回false,這是login方法就會引發異常。而在期望表達式的設置時,確實將其返回值設置為 false,因此測試方法將正確執行,因為login方法確實會拋出DatabaseConnException異常。 針對類進行JMock測試 上述例子是針對接口進行Mock測試的例子,這也是通常使用JMock的情 況,但有時也想要針對具體的類進行Mock測試,這種情況的mock測試和針對接口的方式幾乎一致,僅在創建Mock對象時有少許不同,需要在創建 mock對象之前添加如下代碼:context.setImposteriser(ClassImposteriser.INSTANCE);   //需要添加的代碼 final Msg msg = context.mock(Msg.class);    //與接口方法演示的一樣 4.2 Expectations類 Expectations API在線文檔見: http://www.jmock.org/javadoc/2.0.0/org/jmock/Expectations.html Expectations的語法如下表所示: invocation-count (mock-object).method(argument-constraints);  [必需] inSequence(sequence-name); when(state-machine.is(state-name)); will(action); then(state-machine.is(new-state-name)); 針對Mock對象模擬調用的每一個方法,都應該必須有一個應該必須有一個invocation-count子句,其余子句為可選。 下面對這幾個表達式分別作簡要的介紹: invocation-count invocation-count表示期望的方法的調用次數。其可選的表達形式如下表所示: oneof / one 期望調用執行一次且僅一次 exactly(n).of 期望調用執行n次 atLeast(n).of 期望調用執行至少n次 atMost(n).of 期望調用執行至多n次 between(min, max).of 期望調用執行至少min次,至多max次 allowing / ignoring 期望調用執行任意多次,包括0次 never 不期望調用執行 具體使用如下所示: exactly(4).of(subscriber).receive(message);  atMost(2).of(subscriber).receive(message);   atLeast(4).of(subscriber).receive(message);  atLeast(2).of(subscriber).receive(message);   argument-constraints表示對傳入參數的約束條件,可以使精確匹配的,也可以利用with語句定義模糊匹配條件,如下面的對比所示: one (calculator).add(1, 1);    // allowing (calculator).add(with(any(int.class)), with(any(int.class))) 除了any(Class<T> type)參數約束子句外,jmock還提供了一下約束子句equal(n)、same(o)、a(Class<T> type)、aNull(Class<T> type)、aNonNull(Class<T> type)、not(m)、anyOf(m1, m2, …,mn)、allOf(m1,m2,…,mn)等,具體可參加Expectations文檔: http://www.jmock.org/javadoc/2.0.0/org/jmock/Expectations.html inSequence inSequence用於定義多個方法調用的順序,inSequence子句可以定義多個其在測試代碼中出現的執行順序。使用案例如下所示: context.setImposteriser(ClassImposteriser.INSTANCE); final Msg msg = context.mock(Msg.class); publisher.setMsg(msg); final Sequence sequence = context.sequence("sequence"); context.checking(new Expectations() { { oneOf(msg).method1(message); inSequence(sequence);    //按順序執行  will(doAll(returnValue("test1"), returnValue(5))); oneOf(msg).method2(message); inSequence(sequence);     //按順序執行 will(returnValue("yes")); } }); publisher.test(); /*   public void test() { msg.method1("test"); msg.method2("test");   } */ context.assertIsSatisfied(); sequence名稱任意,有該行子句修飾的Expectations表達 式表示需要統計其執行順序,為使用該子句修飾的表示不需要判斷其順序。上述代碼中,sequence相當於一個標識,這個標識修飾的所有方法為一組順序執 行的方法,這組方法的順序與業務邏輯代碼(上述代碼中test方法)中被調用的順序相同時,執行才能成功。 will will表示方法調用返回情況的約束條件。Wil語句還是非常有用的,對於Mock對象模擬調用方法時,就靠will子句返回調用方法返回值,從而驅動JUnit測試的正常執行。Jmock支持的返回約束形式如下表所示: will(returnValue(v)) 期望返回值v will(returnIterator(c)) 期望返回容器c的一個迭代子 will(returnIterator(v1,v2,…,vn)) 期望返回容器c中的元素v1到vn will(throwException(e)) 期望拋出一個異常e will(doAll(a1,a2,…,an)) 與其他will子句組合使用,代表每次調用時執行從a1到an的若干動作,其中任意一個子句返回類型與實際調用內型匹配,則成功 具體使用如下所示:  /*oneOf(msg).print(message); will(returnValue(5));*/ /*oneOf(msg).print(message); will(returnIterator("test1", "what"));*/ /*oneOf(msg).print(message); will(throwException(new IOException("no")));*/ /*oneOf(msg).print(message); will(doAll(returnValue("test1"), returnValue("test2")));*/ when & then 當想要控制某些方法僅當某個條件達到時才能執行的情況時,可以使用when 子句。When子句需要一個狀態作為參數,這個狀態也就相當於一個開關,當這個狀態滿足某種情況時,及開關打開時就執行對應的方法,否則不執行。 Expectations使用一個字符串用於初始化一個狀態(States),然后使用is(str)或者isNot(str)判斷狀態十分滿足情況,從 而控制方法是否執行。具體使用如下所示: //同樣選用上面提到的sequence例子 context.setImposteriser(ClassImposteriser.INSTANCE); final Msg msg = context.mock(Msg.class); publisher.setMsg(msg); final Sequence sequence = context.sequence("sequence"); //字符串”up”為任意取名的,僅states.is(“up”)用於判斷是否滿足條件 final States states = context.states("states").startsAs("up"); context.checking(new Expectations() { { oneOf(msg).method1(message); inSequence(sequence);    //按順序執行  when(states.is("up")); will(doAll(returnValue("test1"), returnValue(5))); then(states.is("down")); oneOf(msg).method2(message); inSequence(sequence);     //按順序執行 when(states.is("up")); will(returnValue("yes")); } }); publisher.test(); context.assertIsSatisfied(); 上述測試用例並不會成功,會報下面的錯誤,其原因就在於 inSequence()子句規定了兩個方法執行的順序,而最終有一個期望的方法並沒有執行。原因就在於then()語句,then子句在方法自行完成 后,會將states中的狀態字符串設置為”down”,而在method2執行時,判斷其狀態字符串是不是”up”時將得到否定的結果,從而該方法不執 行,於是就出現了下面的錯誤。   when子句所設定的條件滿足時執行相應的方法,而then子句是在方法執行完成之后執行。 4.3小結 本章對Jmock做了簡要的介紹,通過實例說明了怎樣通過Jmock協助JUnit進行特定情況的Java源代碼測試,這樣的情況還是比較常見的,比如下列情況可能需要使用到Jmock: 真實對象具有不可確定的行為,產生不可預測的效果   真實對象很難被創建的   真實對象的某些行為很難被觸發   真實對象實際上還不存在的(和其他開發小組或者和新的硬件打交道) 等等 本章接着對Expectations進行了簡單的介紹,其中Expectations一節只是給出了各個子句的意義及簡單的用法,具體在測試時,還需根據具體的需求設計正確的測試邏輯。 5.總結 本文對JUnit和Jmock進行了簡要的介紹,從JUnit和Jmock的環境配置及測試用例的命名方式開始,接着對JUnit和JMock的使用做了 初步介紹,針對JUnit,詳細介紹了測試時經常會用到的斷言類及其使用方式,而對於JMock,詳細介紹了其怎樣與JUnit進行配合,完成某些僅僅由 JUnit完成不了的單元測試的使用方式,通過以上的步驟,應該能夠輕松實現對Java程序的JUnit測試和JMock測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM