簡介
JUnit 是一個用于Java 編程語言的 單元測試 框架和 回歸測試 框架。
JUnit的特點(diǎn):
- 開放的資源框架
- 提供注釋來識別測試方法
- 提供斷言來測試預(yù)期結(jié)果
- 提供測試運(yùn)行來運(yùn)行測試。
- 運(yùn)行速度快潭袱,優(yōu)雅簡潔
- 可以自動運(yùn)行并且檢查自身結(jié)果并提供即時反饋
使用
斷言
斷言是JUnit中主要的功能榄笙,在JUnit中所有的斷言都包含在 Assert 類中。
Assert類中主要方法如下:
方法名 | 方法描述 |
---|---|
assertEquals | 斷言傳入的預(yù)期值與實(shí)際值是相等的 |
assertNotEquals | 斷言傳入的預(yù)期值與實(shí)際值是不相等的 |
assertArrayEquals | 斷言傳入的預(yù)期數(shù)組與實(shí)際數(shù)組是相等的 |
assertNull | 斷言傳入的對象是為空 |
assertNotNull | 斷言傳入的對象是不為空 |
assertTrue | 斷言條件為真 |
assertFalse | 斷言條件為假 |
assertSame | 斷言兩個對象引用同一個對象,相當(dāng)于“==” |
assertNotSame | 斷言兩個對象引用不同的對象,相當(dāng)于“!=” |
assertThat | 斷言實(shí)際值是否滿足指定的條件 |
注意:上面的方法都為Assert的靜態(tài)方法陪白,通過Assert.xxx
調(diào)用缎玫,如果需要直接調(diào)用硬纤,需要加上 import static org.junit.Assert.*;
執(zhí)行順序
在JUnit中通過@BeforeClass
、@Before
赃磨、@Test
筝家、@After
和@AfterClass
注解來確定執(zhí)行順序。
注解 | 介紹 |
---|---|
@beforeClass() |
注解的方法首先執(zhí)行邻辉,并且只執(zhí)行一次溪王。 |
@afterClass() |
注解的方法最后執(zhí)行,并且只執(zhí)行一次值骇。 |
@before() |
注解的方法針對每一個測試用例執(zhí)行莹菱,但是是在執(zhí)行測試用例之前。 |
@after() |
注解的方法針對每一個測試用例執(zhí)行吱瘩,但是是在執(zhí)行測試用例之后道伟。 |
@Test |
在 before() 方法和 after() 方法之間,執(zhí)行每一個測試用例使碾。執(zhí)行順序根據(jù)代碼位置來決定 |
示例如下:
@BeforeClass
public static void beforeClass() {
System.out.println("in before class");
}
//execute only once, in the end
@AfterClass
public static void afterClass() {
System.out.println("in after class");
}
//execute for each test, before executing test
@Before
public void before() {
System.out.println("in before");
}
//execute for each test, after executing test
@After
public void after() {
System.out.println("in after");
}
//test case 1
@Test
public void testCase1() {
System.out.println("in test case 1");
}
//test case 2
@Test
public void testCase2() {
System.out.println("in test case 2");
}
結(jié)果如圖:
編寫測試
下面是我們要測試的加密程序
public class Encryption {
/**
* 加密程序蜜徽,算法是將字符串反轉(zhuǎn),如 abc 會轉(zhuǎn)為為 cba
* @param content
* @return
*/
public static String encryption(String content){
if (content == null)throw new NullPointerException();
if (content.length() < 2)return content;
char[] data = content.toCharArray();
int size = data.length;
for (int i = 0; i < size/2; i++) {
char c = data[i];
data[i] = data[size - i - 1];
data[size - i - 1] = c;
}
return new String(data);
}
}
測試代碼如下
public class EncryptionTest {
String content = "abc";
@Test
public void encryption() {
assertEquals(Encryption.encryption(content),"cba");
}
}
參數(shù)化測試
上面的測試代碼,每次測試一個方法都要去設(shè)置對應(yīng)的值票摇,非常麻煩拘鞋。Junit 4 引入了一個新的功能參數(shù)化測試。參數(shù)化測試允許開發(fā)人員使用不同的值反復(fù)運(yùn)行同一個測試矢门。你將遵循 5 個步驟來創(chuàng)建參數(shù)化測試盆色。
- 用
@RunWith(Parameterized.class)
來注解EncryptionTest
類。 - 創(chuàng)建一個由
@Parameterized.Parameters
注解的公共的靜態(tài)方法祟剔,它返回一個對象的集合(數(shù)組)來作為測試數(shù)據(jù)集合隔躲。 - 創(chuàng)建EncryptionTest的公共構(gòu)造函數(shù),它接受和一行測試數(shù)據(jù)相等同的東西物延。
- 為每一列測試數(shù)據(jù)創(chuàng)建一個實(shí)例變量蹭越。
- 用實(shí)例變量作為測試數(shù)據(jù)的來源來創(chuàng)建你的測試用例。
代碼如下:
@RunWith(Parameterized.class)
public class EncryptionTest {
String result;
String content;
public EncryptionTest(String content, String result) {
this.content = content;
this.result = result;
}
@Parameterized.Parameters
public static Collection primeNumbers(){
return Arrays.asList(new Object[][]{
{"abc","cba"},
{"1234","4321"},
{"",""}
});
}
@Test
public void encryption() {
System.out.println("content = "+content+" result = "+result);
assertEquals(Encryption.encryption(content),result);
}
}
測試結(jié)果如圖:
異常測試
當(dāng)我們輸入的content = null
時教届,會拋出異常。那么我們怎么測試異常情況驾霜,這個就要使用@Test
注解的expected
參數(shù)案训,參數(shù)值為要檢測的異常。拋出了對應(yīng)的異常則測試成功粪糙,反之則測試失敗强霎。
代碼如下:
@Test(expected = NullPointerException.class)
public void encryption() {
System.out.println("content = "+content+" result = "+result);
assertEquals(Encryption.encryption(content),result);
}
時間測試
有時候我們要檢測某一模塊的效率,就需要進(jìn)行運(yùn)行時間的測試蓉冈。我們可以通過@Test的timeout
參數(shù)城舞,確定最大的運(yùn)行時間轩触,單位為毫秒。
代碼如下:
@Test(timeout = 1000)
public void encryption() {
System.out.println("content = "+content+" result = "+result);
assertEquals(Encryption.encryption(content),result);
}
異步測試
原理:異步測試的原理就是阻塞測試線程家夺,等異步的線程執(zhí)行完后脱柱,再執(zhí)行測試線程。
這里用wait
和notiftyAll
方法來實(shí)現(xiàn),代碼如下
/**
*要測試的類
*/
public class Simple {
public interface Callback{
void finish(String result);
}
private Callback callback;
public void setCallback(Callback callback) {
this.callback = callback;
}
public void deal(){
new Thread(){
@Override
public void run() {
try {
//模擬耗時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
callback.finish("完成");
}
}.start();
}
}
/**
*測試類
*/
public class SimpleTest {
private Object lock = new Object();
private int flag = 0;
String res = null;
@Test
public void deal(){
Simple simple = new Simple();
simple.setCallback(result -> {
synchronized (lock){
flag = 1;
lock.notifyAll();
}
res = result;
});
simple.deal();
synchronized (lock){
while (flag == 0){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
assertEquals(res,"完成");
}
}
assertThat
轉(zhuǎn)自Android單元測試(一):JUnit框架的使用的assertThat
的使用
上面我們所用到的一些基本的斷言拉馋,如果我們沒有設(shè)置失敗時的輸出信息榨为,那么在斷言失敗時只會拋出AssertionError,無法知道到底是哪一部分出錯煌茴。而assertThat就幫我們解決了這一點(diǎn)随闺。它的可讀性更好。
assertThat(T actual, Matcher<? super T> matcher);
assertThat(String reason, T actual, Matcher<? super T> matcher);
其中reason為斷言失敗時的輸出信息蔓腐,actual為斷言的值矩乐,matcher為斷言的匹配器。
常用的匹配器整理:
匹配器 | 說明 | 例子 |
---|---|---|
is | 斷言參數(shù)等于后面給出的匹配表達(dá)式 | assertThat(5, is (5)); |
not | 斷言參數(shù)不等于后面給出的匹配表達(dá)式 assertThat(5, not(6)); | |
equalTo | 斷言參數(shù)相等 | assertThat(30, equalTo(30)); |
equalToIgnoringCase | 斷言字符串相等忽略大小寫 | assertThat(“Ab”, equalToIgnoringCase(“ab”)); |
containsString | 斷言字符串包含某字符串 | assertThat(“abc”, containsString(“bc”)); |
startsWith | 斷言字符串以某字符串開始 | assertThat(“abc”, startsWith(“a”)); |
endsWith | 斷言字符串以某字符串結(jié)束 | assertThat(“abc”, endsWith(“c”)); |
nullValue | 斷言參數(shù)的值為null | assertThat(null, nullValue()); |
notNullValue | 斷言參數(shù)的值不為null | assertThat(“abc”, notNullValue()); |
greaterThan | 斷言參數(shù)大于 | assertThat(4, greaterThan(3)); |
lessThan | 斷言參數(shù)小于 | assertThat(4, lessThan(6)); |
greaterThanOrEqualTo | 斷言參數(shù)大于等于 | assertThat(4, greaterThanOrEqualTo(3)); |
lessThanOrEqualTo | 斷言參數(shù)小于等于 | assertThat(4, lessThanOrEqualTo(6)); |
closeTo | 斷言浮點(diǎn)型數(shù)在某一范圍內(nèi) | assertThat(4.0, closeTo(2.6, 4.3)); |
allOf | 斷言符合所有條件回论,相當(dāng)于&& | assertThat(4,allOf(greaterThan(3), lessThan(6))); |
anyOf | 斷言符合某一條件散罕,相當(dāng)于或 | assertThat(4,anyOf(greaterThan(9), lessThan(6))); |
hasKey | 斷言Map集合含有此鍵 | assertThat(map, hasKey(“key”)); |
hasValue | 斷言Map集合含有此值 | assertThat(map, hasValue(value)); |
hasItem | 斷言迭代對象含有此元素 | assertThat(list, hasItem(element)); |
下圖為使用assertThat測試失敗時所顯示的具體錯誤信息⊥父穑可以看到錯誤信息很詳細(xì)笨使!
當(dāng)然了匹配器也是可以自定義的。這里我自定義一個字符串是否是手機(jī)號碼的匹配器來演示一下僚害。
只需要繼承BaseMatcher抽象類硫椰,實(shí)現(xiàn)matches與describeTo方法。代碼如下:
public class IsMobilePhoneMatcher extends BaseMatcher<String> {
/**
* 進(jìn)行斷言判定萨蚕,返回true則斷言成功靶草,否則斷言失敗
*/
@Override
public boolean matches(Object item) {
if (item == null) {
return false;
}
Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
Matcher matcher = pattern.matcher((String) item);
return matcher.find();
}
/**
* 給期待斷言成功的對象增加描述
*/
@Override
public void describeTo(Description description) {
description.appendText("預(yù)計(jì)此字符串是手機(jī)號碼!");
}
/**
* 給斷言失敗的對象增加描述
*/
@Override
public void describeMismatch(Object item, Description description) {
description.appendText(item.toString() + "不是手機(jī)號碼岳遥!");
}
}
@Rule用法
轉(zhuǎn)自Android單元測試(一):JUnit框架的使用的@Rule用法
還記得一開始我們在@Before與@After注解的方法中加入”測試開始”的提示信息嗎奕翔?假如我們一直需要這樣的提示,那是不是需要每次在測試類中去實(shí)現(xiàn)它浩蓉。這樣就會比較麻煩派继。這時你就可以使用@Rule來解決這個問題,它甚至比@Before與@After還要強(qiáng)大捻艳。
自定義@Rule很簡單驾窟,就是實(shí)現(xiàn)TestRule 接口,實(shí)現(xiàn)apply方法认轨。代碼如下:
public class MyRule implements TestRule {
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
// evaluate前執(zhí)行方法相當(dāng)于@Before
String methodName = description.getMethodName(); // 獲取測試方法的名字
System.out.println(methodName + "測試開始绅络!");
base.evaluate(); // 運(yùn)行的測試方法
// evaluate后執(zhí)行方法相當(dāng)于@After
System.out.println(methodName + "測試結(jié)束!");
}
};
}
}
我們使用一下我們自定義的MyRule,效果如圖:
JUnit - 框架擴(kuò)展
Cactus
Cactus 是一個簡單框架用來測試服務(wù)器端的 Java 代碼(Servlets, EJBs, Tag Libs, Filters)恩急。Cactus 的設(shè)計(jì)意圖是用來減小為服務(wù)器端代碼寫測試樣例的成本杉畜。它使用 JUnit 并且在此基礎(chǔ)上進(jìn)行擴(kuò)展。Cactus 實(shí)現(xiàn)了 in-container 的策略衷恭,意味著可以在容器內(nèi)部執(zhí)行測試此叠。
JWebUnit
JWebUnit 是一個基于 Java 的用于 web 應(yīng)用的測試框架。它以一種統(tǒng)一匾荆、簡單測試接口的方式包裝了如 HtmlUnit 和 Selenium 這些已經(jīng)存在的框架來允許你快速地測試 web 應(yīng)用程序的正確性拌蜘。
JWebUnit 提供了一種高級別的 Java API 用來處理結(jié)合了一系列驗(yàn)證程序正確性的斷言的 web 應(yīng)用程序。這包括通過鏈接牙丽,表單的填寫和提交简卧,表格內(nèi)容的驗(yàn)證和其他 web 應(yīng)用程序典型的業(yè)務(wù)特征。
XMLUnit
XMLUnit 提供了一個單一的 JUnit 擴(kuò)展類烤芦,即 XMLTestCase举娩,還有一些允許斷言的支持類:
- 比較兩個 XML 文件的不同(通過使用 Diff 和 DetailedDiff 類)
- 一個 XML 文件的驗(yàn)證(通過使用 Validator 類)
- 使用 XSLT 轉(zhuǎn)換一個 XML 文件的結(jié)果(通過使用 Transform 類)
- 對一個 XML 文件 XPath 表達(dá)式的評估(通過實(shí)現(xiàn) XpathEngine 接口)
- 一個 XML 文件進(jìn)行 DOM Traversal 后的獨(dú)立結(jié)點(diǎn)(通過使用 NodeTest 類)
MockObject
在一個單元測試中,虛擬對象可以模擬復(fù)雜的构罗,真實(shí)的(非虛擬)對象的行為铜涉,因此當(dāng)一個真實(shí)對象不現(xiàn)實(shí)或不可能包含進(jìn)一個單元測試的時候非常有用。