單元測(cè)試框架:JUnit

簡(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 upclean 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ū)別的斷言方法:assertThatassertThat的參數(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

Matchers and assertThat

  • 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è)文檔:

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é)果如下:

result

從上面的測(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í)行下去。

從上面的分析中可以看出,TheoriesParameterized 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è)例子:

  1. 首先我們需要定義一個(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è)試用例。

  1. 定義好了測(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");
        }
    }
}
  1. 最后,再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ì)上面的類Mathadd方法進(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>
創(chuàng)建測(cè)試用例
  • 最后,寫(xiě)上測(cè)試代碼,進(jìn)行測(cè)試就可以了。

更多詳細(xì)信息,請(qǐng)查看官網(wǎng):Building Local Unit Tests

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容