簡(jiǎn)介
測(cè)試 在軟件開(kāi)發(fā)中是一個(gè)很重要的方面,良好的測(cè)試可以在很大程度決定一個(gè)應(yīng)用的命運(yùn)。
軟件測(cè)試中,主要有3大種類:
-
單元測(cè)試
單元測(cè)試主要是用于測(cè)試程序模塊,確保代碼運(yùn)行正確。單元測(cè)試是由開(kāi)發(fā)者編寫(xiě)并進(jìn)行運(yùn)行測(cè)試。一般使用的測(cè)試框架是 JUnit 或者 TestNG。測(cè)試用例一般是針對(duì)方法 級(jí)別的測(cè)試。 -
集成測(cè)試
集成測(cè)試用于檢測(cè)系統(tǒng)是否能正常工作。集成測(cè)試也是由開(kāi)發(fā)者共同進(jìn)行測(cè)試,與單元測(cè)試專注測(cè)試個(gè)人代碼組件不同的是,集成測(cè)試是系統(tǒng)進(jìn)行跨組件測(cè)試。 -
功能性測(cè)試
功能性測(cè)試是一種質(zhì)量保證過(guò)程以及基于測(cè)試軟件組件的規(guī)范下的由輸入得到輸出的一種黑盒測(cè)試。功能性測(cè)試通常由不同的測(cè)試團(tuán)隊(duì)進(jìn)行測(cè)試,測(cè)試用例的編寫(xiě)要遵循組件規(guī)范,然后根據(jù)測(cè)試輸入得到的實(shí)際輸出與期望值進(jìn)行對(duì)比,判斷功能是否正確運(yùn)行。
概述
本文只對(duì) 單元測(cè)試 進(jìn)行介紹,主要介紹如何在 Android Studio 下進(jìn)行單元測(cè)試,單元測(cè)試使用的測(cè)試框架為 JUnit
好處
可能目前仍有很大一部分開(kāi)發(fā)者未使用 單元測(cè)試 對(duì)他們的代碼進(jìn)行測(cè)試,一方面可能是覺(jué)得沒(méi)有必要,因?yàn)榧词箾](méi)有進(jìn)行單元測(cè)試,程序照樣運(yùn)行得很好;另一方面,也許有些人也認(rèn)同單元測(cè)試的好處,但是由于需要額外的學(xué)習(xí)成本,所以很多人也是沒(méi)有時(shí)間或者說(shuō)是沒(méi)有耐心進(jìn)行學(xué)習(xí)······
這里我想說(shuō)的是,如果大家去看下 github 上目前主流的開(kāi)源框架,star 數(shù)比較多的項(xiàng)目,一般都有很詳盡的測(cè)試用例。所以說(shuō),單元測(cè)試對(duì)于我們的項(xiàng)目開(kāi)發(fā),還是挺有好處的。
至于單元測(cè)試的好處,我這里提及幾點(diǎn):
- 保證代碼運(yùn)行與我們預(yù)想的一樣,代碼正確性可以得到保證
- 程序運(yùn)行出錯(cuò)時(shí),有利于我們對(duì)錯(cuò)誤進(jìn)行查找(因?yàn)槲覀兒雎晕覀儨y(cè)試通過(guò)的代碼)
- 有利于提升代碼架構(gòu)設(shè)計(jì)(用于測(cè)試的用例應(yīng)力求簡(jiǎn)單低耦合,因此編寫(xiě)代碼的時(shí)候,開(kāi)發(fā)者往往會(huì)為了對(duì)代碼進(jìn)行測(cè)試,將其他耦合的部分進(jìn)行解耦處理)
······
JUnit 簡(jiǎn)介
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
JUnit 是一個(gè)支持可編寫(xiě)重復(fù)測(cè)試用例的簡(jiǎn)單框架。它是 xUnit 單元測(cè)試框架架構(gòu)的一個(gè)子集。
名稱 | 解釋 |
---|---|
Assertions | 單元測(cè)試實(shí)用方法 |
Test Runners | 測(cè)試實(shí)例應(yīng)當(dāng)怎樣被執(zhí)行(測(cè)試運(yùn)行器) |
Aggregating tests in Suites | 合并多個(gè)相關(guān)測(cè)試用例到一個(gè)測(cè)試套件中(當(dāng)運(yùn)行測(cè)試套件時(shí),相關(guān)用例就會(huì)一起被執(zhí)行) |
Test Execution Order | 指定測(cè)試用例運(yùn)行順序 |
Exception Testing | 如何指定測(cè)試用例期望的異常 |
Matchers and assertThat | 如何使用 Hamcrest 的匹配器 (matchers ) 和更加具備描述性的斷言 (assertions ) |
Ignoring Tests | 失能類或方法的測(cè)試用例 |
Timeout for Tests | 指定測(cè)試用例的最大運(yùn)行時(shí)間(超過(guò)這個(gè)時(shí)間,自動(dòng)結(jié)束測(cè)試用例) |
Parameterized Tests | 測(cè)試用例運(yùn)行多次,每次都使用不同的參數(shù)值 |
Assumptions with Assume | 類似斷言,但不會(huì)使測(cè)試用例失敗 |
Rules | 為測(cè)試用例增加Rules (相當(dāng)于添加功能) |
Theories | 使用隨機(jī)生成的數(shù)據(jù)使測(cè)試用例更加科學(xué)嚴(yán)謹(jǐn) |
Test Fixtures | 為測(cè)試方法或者類指定預(yù)備的set up 和clean up 方法 |
Categories | 將測(cè)試用例組織起來(lái),方便過(guò)濾 |
··· | ··· |
Assertions - 斷言
JUnit 為所有的原始類型和對(duì)象,數(shù)組(原始類型數(shù)組或者對(duì)象數(shù)組)提供了多個(gè)重載的斷言方法(assertion method
)。斷言方法的參數(shù)第一個(gè)為預(yù)期值,第二個(gè)為實(shí)際運(yùn)行的值。另一個(gè)可選方法的第一個(gè)參數(shù)是作為失敗輸出的字符串信息。還有一個(gè)稍微有些區(qū)別的斷言方法:assertThat
。assertThat
的參數(shù)有一個(gè)可選的失敗信息輸出,實(shí)際運(yùn)行的值和一個(gè) Matcher
對(duì)象。請(qǐng)知悉assertThat
的預(yù)期值和實(shí)際運(yùn)行值與其他的斷言方法位置是相反的。
ps:實(shí)際開(kāi)發(fā)中,建議采用 Hamcrest 提供的斷言方法:assertThat
,因?yàn)檫@個(gè)方法一方面寫(xiě)出的代碼更具可讀性,一方面當(dāng)斷言失敗時(shí),這個(gè)方法會(huì)給出具體的錯(cuò)誤提示信息。
更多的 Assertions 信息,請(qǐng)查看文檔:Assert
Test Runners - 測(cè)試運(yùn)行器
當(dāng)一個(gè)類被注解@RunWith
或者集成一個(gè)被@RunWith
注解的類時(shí),JUnit 會(huì)把測(cè)試用例運(yùn)行在該類上,而不是內(nèi)置的運(yùn)行器上。
ps: JUnit 的默認(rèn)運(yùn)行器是 BlockJUnit4ClassRunner
。
如果類注解為@RunWith(JUnit4.class)
,則使用的是默認(rèn)的測(cè)試運(yùn)行器 BlockJUnit4ClassRunner
。
更多詳細(xì)信息,請(qǐng)查看文檔:@RunWith
Aggregating tests in Suites - 測(cè)試套件
使用套件(Suite
)作為運(yùn)行器使得你可以手動(dòng)建造一個(gè)可以容納許多類的測(cè)試用例。使用測(cè)試套件時(shí),你需要?jiǎng)?chuàng)建一個(gè)類,然后為其注解上@RunWith(Suite.class)
和@SuiteClasses(TestClass1.class, ...)
,這樣,當(dāng)你運(yùn)行這個(gè)類時(shí),測(cè)試套件各個(gè)類的測(cè)試用例就會(huì)全部被執(zhí)行。
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestFeatureLogin.class,
TestFeatureLogout.class,
TestFeatureNavigate.class,
TestFeatureUpdate.class
})
public class FeatureTestSuite {
// the class remains empty,
// used only as a holder for the above annotations
}
Test Execution Order
從 JUnit 4.11版本開(kāi)始,JUnit 默認(rèn)使用確定的,不可預(yù)見(jiàn)性的測(cè)試用例執(zhí)行順序(MethodSorters.DEFAULT
)。要改變測(cè)試用例執(zhí)行順序,只需簡(jiǎn)單為測(cè)試類添加@FixMethodOrder
注解,并指定一個(gè)方法排序規(guī)則:
@FixMethodOrder(MethodSorters.JVM)
:由JVM決定方法執(zhí)行順序,在不同的JVM上,執(zhí)行順序可能不同。
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
:按方法名進(jìn)行排序(字典序)進(jìn)行執(zhí)行。
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestMethodOrder {
@Test
public void testA() {
System.out.println("first");
}
@Test
public void testB() {
System.out.println("second");
}
@Test
public void testC() {
System.out.println("third");
}
}
Exception Testing
你如何驗(yàn)證代碼拋出的異常是你所期望的?驗(yàn)證代碼正常走完是很重要,但是確保代碼在異常情況下表現(xiàn)也與預(yù)期一樣也是很重要的,比如:
new ArrayList<Object>().get(0);
這句代碼應(yīng)該拋出一個(gè) IndexOutOfBoundsException
異常。@Test
注解有一個(gè)可選的參數(shù) expected
,它可以攜帶一個(gè)Throwable
的子類。如果我們希望驗(yàn)證ArrayList
能正確拋出一個(gè)異常,我們應(yīng)該這樣寫(xiě):
@Test(expected = IndexOutOfBoundsException.class)
public void empty() {
new ArrayList<Object>().get(0);
}
參數(shù)expected
的使用應(yīng)該慎重。只要測(cè)試代碼中的任何一句拋出一個(gè)IndexOutOfBoundsException
異常,那么上面的測(cè)試用例就會(huì)通過(guò)。對(duì)于代碼比較長(zhǎng)的測(cè)試用例,推薦使用 ExpectedException
規(guī)則。
更多詳情,請(qǐng)查看:Exception testing
-
assertThat
的一個(gè)通用格式為:
assertThat([value], [matcher statement])
示例:
assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));
assertThat
的第二個(gè)參數(shù)是一個(gè)Matcher
.
詳細(xì)的Matcher
介紹,可以查看以下兩個(gè)文檔:
-
JUnit Matchers:JUnit 提供的
Matcher
-
Hamcrest CoreMatchers:Hamcrest 提供的
Matcher
Ignoring Tests
由于某些原因,你不希望測(cè)試用例運(yùn)行失敗,你只想忽略它,那你只需暫時(shí)失能這個(gè)測(cè)試用例即可。
在 JUnit 中,你可以通過(guò)注釋方法或者刪除@Test
注解來(lái)忽略測(cè)試用例;但是這樣的話測(cè)試運(yùn)行器就不會(huì)對(duì)該測(cè)試用例進(jìn)行相關(guān)報(bào)告。另一個(gè)方案是為測(cè)試用例在@Test
注解前面或后面添加上@Ignore
注解;那么測(cè)試運(yùn)行器運(yùn)行后,就會(huì)輸出相關(guān)測(cè)試用例忽略數(shù)目,運(yùn)行所有測(cè)試用例的數(shù)目和測(cè)試用例失敗的數(shù)目顯示。
注意下@Ignore
注解可以攜帶一個(gè)可選參數(shù)(String
類型),如果你想記錄測(cè)試用例忽略的原因,可以使用這個(gè)參數(shù):
@Ignore("Test is ignored as a demonstration")
@Test
public void testSame() {
assertThat(1, is(1));
}
Timeout for Tests
對(duì)于失控或者運(yùn)行時(shí)間太長(zhǎng)的測(cè)試用例,則自動(dòng)被認(rèn)為失敗,有兩種方法可以實(shí)現(xiàn)這個(gè)動(dòng)作。
- 為
@Test
增加timeout
參數(shù)
你可以為一個(gè)測(cè)試用例指定一個(gè)超時(shí)時(shí)間(毫秒),在規(guī)定時(shí)間內(nèi),如果測(cè)試用例沒(méi)有運(yùn)行結(jié)束,那么測(cè)試用例運(yùn)行所在線程就會(huì)拋出一個(gè)異常,從而引起測(cè)試失敗。
@Test(timeout=1000)
public void testWithTimeout() {
...
}
這種實(shí)現(xiàn)方式是通過(guò)將測(cè)試用例方法運(yùn)行在另一個(gè)單獨(dú)的線程中。如果測(cè)試用例運(yùn)行時(shí)間超過(guò)規(guī)定的時(shí)間,那么測(cè)試用例就會(huì)失敗,JUnit 就會(huì)打斷執(zhí)行測(cè)試用例的線程。如果測(cè)試用例內(nèi)部執(zhí)行有可以中斷的操作,那么運(yùn)行測(cè)試用例的線程就會(huì)退出(如果測(cè)試用例內(nèi)部是一個(gè)無(wú)限循環(huán),那么運(yùn)行測(cè)試用例的線程將會(huì)永遠(yuǎn)運(yùn)行,而其他測(cè)試用例仍在其他的線程上執(zhí)行)。
-
Timeout Rule (應(yīng)用到測(cè)試類的所有測(cè)試用例)
Timeout Rule
會(huì)將同一個(gè)超時(shí)時(shí)間應(yīng)用到測(cè)試類的所有測(cè)試方法中,并且如果測(cè)試用例@Test
帶有timeout
參數(shù),則會(huì)疊加到一起(實(shí)際測(cè)試中,并沒(méi)有疊加的效果,甚至tiemout
參數(shù)并不生效,依舊還是以Timeout Rule
為準(zhǔn))
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
public class HasGlobalTimeout {
public static String log;
private final CountDownLatch latch = new CountDownLatch(1);
@Rule
public Timeout globalTimeout = Timeout.seconds(10); // 10 seconds max per method tested
@Test
public void testSleepForTooLong() throws Exception {
log += "ran1";
TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}
@Test
public void testBlockForever() throws Exception {
log += "ran2";
latch.await(); // will block
}
}
Timeout rule
指定的超時(shí)時(shí)間timeout
會(huì)應(yīng)用到所有的測(cè)試用例中,包括任何的@Before
和@After
方法。如果測(cè)試方法是一個(gè)無(wú)限循環(huán)(或者是無(wú)法響應(yīng)中斷操作),那么@Afte
注解的方法永遠(yuǎn)不會(huì)被執(zhí)行。
Parameterized Tests - 參數(shù)化測(cè)試
對(duì)于單元測(cè)試來(lái)說(shuō),如果想要同一個(gè)測(cè)試用例中測(cè)試多組不同的數(shù)據(jù),那么只能手動(dòng)執(zhí)行一次后,更改數(shù)據(jù),再進(jìn)行執(zhí)行,而使用參數(shù)化測(cè)試的話,則可以將上述的行為進(jìn)行自動(dòng)化,我們所需要做的就是提供一個(gè)數(shù)據(jù)集合,然后創(chuàng)建相應(yīng)的成員變量用來(lái)接收數(shù)據(jù)集合傳遞過(guò)來(lái)的數(shù)據(jù)(在測(cè)試類構(gòu)造器中接收),最后運(yùn)行測(cè)試用例時(shí),參數(shù)化測(cè)試運(yùn)行器就會(huì)依次從數(shù)據(jù)集合中取出一個(gè)數(shù)據(jù),并傳給測(cè)試用例運(yùn)行:
//功能類
public class Math {
public static int add(int a, int b) {
return a + b;
}
}
//單元測(cè)試類
@RunWith(Parameterized.class) //指定參數(shù)化測(cè)試運(yùn)行器
public class MathTest {
private int a; //聲明成員變量用于接收數(shù)據(jù)
private int b;
public MathTest(int a, int b) { //接受集合數(shù)據(jù)
this.a = a;
this.b = b;
}
@Parameterized.Parameters //創(chuàng)建參數(shù)集合
public static Collection<Object[]> data() {
Collection<Object[]> collection = new ArrayList<>();
collection.add(new Object[]{1, 2});
collection.add(new Object[]{10, 20});
collection.add(new Object[]{30, 40});
return collection;
}
@Test
public void add() throws Exception {
assertThat(Math.add(a, b), is(equalTo(30)));
}
}
Assumptions with Assume - 前置條件
前置條件與斷言類似,只是斷言在不匹配時(shí),測(cè)試用例就會(huì)失敗,而前置條件在不匹配時(shí)只會(huì)使測(cè)試用例退出。
前置條件的使用場(chǎng)景是:當(dāng)你的代碼在不同的環(huán)境下,可能有不同的結(jié)果時(shí),如果你明確后續(xù)的測(cè)試代碼是基于某一特定的環(huán)境下,才進(jìn)行測(cè)試,那么,借助前置條件,就可以實(shí)現(xiàn)所需功能。
比如,假設(shè) Windows 平臺(tái)的文件路徑分隔符為"\",而 Linux 平臺(tái)的為"/”,假設(shè)我們的測(cè)試用例只想在 Linux 平臺(tái)上進(jìn)行測(cè)試,那么:
@Test
public void filenameIncludesUsername() {
assumeThat(File.separatorChar, is('/'));
assertThat(new User("optimus").configFileName(), is("configfiles/optimus.cfg"));
}
如果在 Windows 平臺(tái)運(yùn)行測(cè)試用例時(shí),assumeThat(File.separatorChar, is('/'))
就會(huì)不匹配,那么測(cè)試用例就直接退出(類似異常機(jī)制)。
Rules - 規(guī)則
Rules
允許為測(cè)試用例增加靈活的條件或者是重新定義每個(gè)類的測(cè)試用例行為。測(cè)試類可以重新或者繼承一下任一提供的Rules
,或者自己自定義一個(gè)。
Rule | Description |
---|---|
TemporaryFolder | 創(chuàng)建臨時(shí)文件夾/文件(測(cè)試方法完成后文件被自動(dòng)刪除) |
ExternalResource | 外部資源Rules 的一個(gè)基類 |
ErrorCollector | 收集錯(cuò)誤信息 |
Verifier | 具備校驗(yàn)功能的一個(gè)基類 |
TestWatcher | 具備測(cè)試結(jié)果記錄的一個(gè)基類 |
TestName | 該Rules 對(duì)象可在測(cè)試用例內(nèi)部獲取測(cè)試用例方法名 |
Timeout | 為測(cè)試類所有測(cè)試用例約束最長(zhǎng)運(yùn)行時(shí)間 |
ExpectedException | 該類使得測(cè)試用例能在方法內(nèi)判別測(cè)試代碼是否拋出預(yù)期異常 |
ClassRule | 類級(jí)別Rule ,用于靜態(tài)變量的注解,在測(cè)試類運(yùn)行時(shí)只執(zhí)行一次 |
Rule | 方法級(jí)別的Rule ,用于成員變量的注解,在類的每個(gè)測(cè)試用例執(zhí)行時(shí)都會(huì)被執(zhí)行 |
RuleChain | 為多個(gè)Rules 指定順序 |
TestRule | 自定義Rules 基類 |
這里簡(jiǎn)單介紹下自定義Rules
,假設(shè)我們要為所有的測(cè)試用例輸出前后添加"------------",那么,我們需要先創(chuàng)建一個(gè)Rule
:
public class CustomerRule implements TestRule {
@Override
public Statement apply(final Statement base, Description description) {
return new Statement(){
@Override
public void evaluate() throws Throwable {
System.out.println("--------------------------");
base.evaluate();
System.out.println();
System.out.println("--------------------------");
}
};
}
}
然后把自定義的TestRule
運(yùn)用到測(cè)試類里面即可:
@Rule
public CustomerRule customerRule = new CustomerRule();
@Test
public void testCustom() {
assertThat(1, is(1));
}
更多Rules
詳細(xì)信息,請(qǐng)查看:Rules
Theories - 測(cè)試?yán)碚?br>
JUnit 中的 Theories 可以理解成一個(gè)測(cè)試?yán)碚摚摾碚摪褱y(cè)試分為兩部分:一個(gè)是提供測(cè)試數(shù)據(jù)(單個(gè)數(shù)據(jù)用@DataPoint
注解,集合數(shù)據(jù)使用@DataPoints
注解),數(shù)據(jù)提供者必須為靜態(tài)成員/方法;另一個(gè)是理論本身,也即測(cè)試用例方法。
Theories 的測(cè)試用例允許參數(shù)傳遞(普通測(cè)試用例測(cè)試方法不能攜帶參數(shù)),參數(shù)傳遞規(guī)則是首先從數(shù)據(jù)集合中取出一個(gè)作為第一個(gè)參數(shù),然后依次取出集合的元素(包含已作為參數(shù)1的那個(gè)數(shù)據(jù))作為第二個(gè)參數(shù)····
看下下面的測(cè)試用例就會(huì)比較清楚 Theories 的運(yùn)作流程:
@RunWith(Theories.class)
public class MathTest {
// @DataPoint
// public static int arg0 = 1;
// @DataPoint
// public static int arg1 = 10;
// @DataPoint
// public static int arg2 = 0;
@DataPoints
public static int[] args = new int[]{1, 10, 0};
@Theory
public void divied(int a, int b) throws Exception {
Assume.assumeTrue(b != 0);
System.out.println(String.format("a=%d,b=%d", a, b));
assertThat(Math.divied(a, b), not(equalTo(2)));
}
}
運(yùn)行結(jié)果如下:
從上面的測(cè)試用例可以看出,
MathTest
提供的數(shù)據(jù)集合為{1,10,0}
,所以:第一次 運(yùn)行測(cè)試用例
divied(int a, int b)
時(shí),從集合中取出一個(gè)參數(shù),即1
會(huì)傳遞給參數(shù)a
,然后又從集合中取出一個(gè)參數(shù),也是1
,傳遞給b
,然后執(zhí)行測(cè)試用例;第二次 運(yùn)行時(shí),參數(shù)
a
保持不變,然后從新從集合中取出下一個(gè)元素給到b
,所以b=10
,然后執(zhí)行測(cè)試用例;第三次 運(yùn)行時(shí),參數(shù)
a
保持不變,然后從新從集合中取出下一個(gè)元素給到b
,所以b=0
,然后執(zhí)行測(cè)試用例時(shí),由于不滿足Assume
前置條件,故測(cè)試用例不再往下運(yùn)行,直接退出,所以看到當(dāng)b=0
時(shí),沒(méi)有打印結(jié)果;第四次 運(yùn)行時(shí),由于
b
在前面第一輪運(yùn)行時(shí)已完整取出了整個(gè)集合數(shù)據(jù),所以此時(shí)就輪到參數(shù)a
取出集合的下一個(gè)數(shù)據(jù),即a=10
,然后就按照前一輪的執(zhí)行邏輯繼續(xù)執(zhí)行下去。
從上面的分析中可以看出,Theories 與 Parameterized Tests 很類似,兩者都實(shí)現(xiàn)了多組數(shù)據(jù)共同作用于同一個(gè)測(cè)試用例的功能,不過(guò)兩者的參數(shù)傳遞機(jī)制還是有很大的不同的, Parameterized Tests 可以提供多維數(shù)組的形式符合參數(shù)個(gè)數(shù)順序,而 Theories 的參數(shù)集合中的每個(gè)元素都會(huì)同時(shí)作用于各個(gè)參數(shù);個(gè)人感覺(jué)還是 Parameterized Tests 更符合通常的測(cè)試邏輯。
Test Fixtures - 測(cè)試設(shè)備
Test Fixtures 是被用作測(cè)試用例運(yùn)行的基準(zhǔn)的一系列對(duì)象的混合狀態(tài),Test Fixtures 為我們提供了4個(gè)注解(均用于方法上):
Annotation | Description |
---|---|
@BeforeClass | 測(cè)試類運(yùn)行時(shí)執(zhí)行 |
@AfterClass | 測(cè)試類結(jié)束時(shí)執(zhí)行 |
@Before | 每個(gè)測(cè)試用例執(zhí)行前先執(zhí)行 |
@After | 每個(gè)測(cè)試用例執(zhí)行后再執(zhí)行 |
Categories - 分類
Categories 見(jiàn)名知意,就是將一系列測(cè)試類/測(cè)試方法進(jìn)行分類,每個(gè)類或者接口都可以作為一個(gè)Category
,且支持類別繼承。
比如,你指定一個(gè)測(cè)試用例屬于SuperClass.class
的類別(使用@Category(SuperClass.class)
注解在測(cè)試類用例上),然后@IncludeCategory(SuperClass.class)
,那么任何測(cè)試用例上注解了@Category(SuperClass.class)
或者@Category({SubClass.class})
的方法都會(huì)被執(zhí)行。
舉個(gè)例子:
- 首先我們需要定義一個(gè)或多個(gè)測(cè)試類別(即
Category
)
public class Category {
public static interface Category01 {}
public static interface Category02 {}
public static interface Category01Impl extends Category01{}
}
這里有3種測(cè)試Category
,其中,類別Category01Impl
繼承了類別Category01
,所以任何@IncludeCategory(Category01.class)
的測(cè)試類,測(cè)試時(shí)也會(huì)執(zhí)行類別為Category01Impl
的測(cè)試用例。
- 定義好了測(cè)試類別后,我們就需要將這些類別運(yùn)用到測(cè)試類或者測(cè)試用例上
public class Tests {
public static class Test01 {
@Test
@Category(Category01.class) //運(yùn)用到測(cè)試用例上
public void test01() {
System.out.println("This testCase belongs to Category01");
}
@Test
@Category(Category01Impl.class)//運(yùn)用到測(cè)試用例上
public void test01Impl() {
System.out.println("This testCase belongs to Category01Impl");
}
}
@Category(Category02.class)//運(yùn)用到測(cè)試類上,類中所有測(cè)試方法都屬于`Category02.class`這個(gè)類別
public static class Test02 {
@Test
public void test02() {
System.out.println("This testCase belongs to Category02");
}
}
}
- 最后,再
Categories
類別測(cè)試運(yùn)行器上運(yùn)行需要的測(cè)試用例即可
@RunWith(Categories.class)
@IncludeCategory(Category01.class)
@SuiteClasses({Tests.Test01.class, Tests.Test02.class}) // Note that Category is a kind of Suite
public class CategoryTest {
}
更多詳細(xì)信息,請(qǐng)查看:Categories
Android Studio 進(jìn)行單元測(cè)試
假設(shè)我們需要對(duì)一個(gè) Java Module 進(jìn)行單元測(cè)試,采用 JUnit 框架,則部署步驟如下:
- 在
build.gralde
中依賴 JUnit:
dependencies {
testImplementation 'junit:junit:4.12' //or testCompile
}
- 創(chuàng)建一個(gè)類
public class Math {
public static int add(int a, int b) {
return a + b;
}
}
- 對(duì)上面的類
Math
的add
方法進(jìn)行測(cè)試
我們可以手動(dòng)創(chuàng)建一個(gè)Math
的測(cè)試類,但是借助于 Android Studio,我們可以很方面的使用快捷操作自動(dòng)生成測(cè)試類和測(cè)試用例,具體做法為:打開(kāi)要進(jìn)行測(cè)試的類文件,雙擊類名/方法名進(jìn)行選中,然后按快捷鍵:<Ctrl-Shift-T>
- 最后,寫(xiě)上測(cè)試代碼,進(jìn)行測(cè)試就可以了。
更多詳細(xì)信息,請(qǐng)查看官網(wǎng):Building Local Unit Tests