Overview
單元測試(Unit Testing)又稱為模塊測試倒谷,是指對軟件中的最小可測試單元進(jìn)行檢查和驗證莱坎。在過程化編程中衣式,一個單元就是單個程序、函數(shù)檐什、過程 等碴卧;對于面向?qū)ο缶幊蹋钚卧褪?strong>方法乃正,包括基類(超類)住册、抽象類、或者派生類(子類)中的方法瓮具。
Android Unit Testing
Android中的單元測試基于Junit荧飞,可分為 本地測試 Local tests 和 插樁測試 instrumented tests,在項目中對應(yīng)
- module-name/src/test/java/
該目錄下的代碼運(yùn)行在本地JVM上名党,其優(yōu)點(diǎn)是速度快叹阔,不需要設(shè)備或模擬器的支持,但是無法直接運(yùn)行含有Android系統(tǒng)API引用的測試代碼传睹。 - module-name/src/androidTest/java/
該目錄下的測試代碼需要運(yùn)行在Android設(shè)備或模擬器下面耳幢,因此可以使用Android系統(tǒng)的API,速度較慢蒋歌。
Unit Testing Framework
JUnit4
JUnit4注解
JUnit是一套基于注解 的單元測試框架帅掘。在Android studio中委煤,編寫在test目錄下的測試類都是基于該框架實(shí)現(xiàn),該目錄下的測試代碼運(yùn)行在本地的JVM上修档,不需要設(shè)備(真機(jī)或模擬器)的支持碧绞。
JUnit4中常用的幾個注解:
@BeforeClass 測試類里所有用例運(yùn)行之前,運(yùn)行一次這個方法吱窝。方法必須是public static void
@AfterClass 與BeforeClass對應(yīng)
@Before 在每個測試用例運(yùn)行之前都運(yùn)行一次讥邻。
@After 與Before對應(yīng)。
@Ignore 忽略該方法
@Test 指定該方法為測試方法院峡,方法必須是public void兴使。
-
@RunWith 測試類名之前,用來確定這個類的測試運(yùn)行器照激。
image.png
編寫測試類
選中要測試的方法->右鍵->go to->Test(快捷鍵:shift+command+t)
填寫Class Name发魄,按需要勾選setUp/tearDown,選擇要測試的方法:
生成測試類代碼俩垃,右鍵->Run 'CalculatorTest with Coverage'開始測試励幼,控制臺查看測試結(jié)果:
public class CalculatorTest {
private Calculator mCalculator;
@Before
public void setUp() throws Exception {
mCalculator = new Calculator();
}
@After
public void tearDown() throws Exception {
}
@Test
public void sum() throws Exception {
assertEquals(7, mCalculator.add(3, 4));
/**
* assertThat(0, is(1)); // fails:
* // failure message:
* // expected: is <1>
* // got value: <0>
* assertThat(0, is(not(1))) // passes
**/
}
@Test
public void sum2() throws Exception {
assertEquals(6, mCalculator.add(3, 4));
}
}
覆蓋測試
使用@Parameters來進(jìn)行單個方法的多次不同參數(shù)的測試,具體如下:
- 1>在測試類上添加@RunWith(Parameterized.class)注解口柳。
- 2>添加構(gòu)造方法苹粟,并將測試的參數(shù)作為其構(gòu)造參數(shù)。
- 3>添加獲取參數(shù)集合的static方法跃闹,并在該方法上添加@Parameters注解嵌削。
- 4>在需要測試的方法中直接使用成員變量,該變量由JUnit通過構(gòu)造方法生成望艺。
@RunWith(Parameterized.class)
public class CalculatorWithParameterizedTest {
/** 參數(shù)的變量 */
private final double mOperandOne;
private final double mOperandTwo;
/** 期待值 */
private final double mExpectedResult;
/** 計算類 */
private Calculator mCalculator;
/**
* 構(gòu)造方法苛秕,框架可以自動填充參數(shù)
*/
public CalculatorWithParameterizedTest(double operandOne, double operandTwo,
double expectedResult){
mOperandOne = operandOne;
mOperandTwo = operandTwo;
mExpectedResult = expectedResult;
}
/**
* 需要測試的參數(shù)和對應(yīng)結(jié)果
*/
@Parameterized.Parameters
public static Collection<Object[]> initData(){
return Arrays.asList(new Object[][]{
{0, 0, 0},
{0, -1, -1},
{2, 2, 4},
{8, 8, 16},
{16, 16, 32},
{32, 0, 32},
{64, 64, 128}});
}
@Before
public void setUp() {
mCalculator = new Calculator();
}
/**
* 使用參數(shù)組測試加的相關(guān)操作
*/
@Test
public void sum() {
double resultAdd = mCalculator.add(mOperandOne, mOperandTwo);
assertThat(resultAdd, is(equalTo(mExpectedResult)));
}
}
套件測試
套件測試說的通俗點(diǎn),就是批量運(yùn)行測試類荣茫。涉及注解@RunWith(Suite.class) 和 @Suite
//被測試類CalculaterTest.class 和 CalculaterTest2.class
@RunWith(Suite.class)
@Suite.SuiteClasses({ CalculaterTest.class, CalculaterTest2.class })
public class SuiteTest {
}
AndroidJUnitRunner
AndroidJUnitRunner類是一個JUnit 測試運(yùn)行器想帅,允許運(yùn)行JUnit 3或JUnit 4測試類在Android設(shè)備上。當(dāng)單元測試中涉及到Android系統(tǒng)庫的調(diào)用時啡莉,你可以通過該方案類完成測試。使用方法是在androidTest目錄下創(chuàng)建測試類旨剥,在該類上添加@RunWith(AndroidJUnit4.class) 注解咧欣。
獲取上下文
在AndroidJUnitRunner中,通過InstrumentationRegistry來獲取Context轨帜。
//獲取application的context
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.lgy.unittest", appContext.getPackageName());
}
}
測試篩選
在 JUnit 4.x 測試中魄咕,您可以使用注解對測試運(yùn)行進(jìn)行配置。此功能可將向測試中添加樣板文件和條件代碼的需求降至最低蚌父。除了 JUnit 4 支持的標(biāo)準(zhǔn)注解外哮兰,測試運(yùn)行器還支持 Android 特定的注解毛萌,包括:
@RequiresDevice 指定測試僅在物理設(shè)備而不在模擬器上運(yùn)行。
@SdkSupress 禁止在低于給定級別的 Android API 級別上運(yùn)行測試喝滞。例如阁将,要禁止在低于 18 的所有 API 級別上運(yùn)行測 試,請使用注解 @SDKSupress(minSdkVersion=18)右遭。
@SmallTest做盅、@MediumTest 和 @LargeTest 指定測試的運(yùn)行時長以及運(yùn)行頻率。
Mockito
Mockito 是一個體驗很好的mocking框架窘哈,它可以讓你寫出漂亮吹榴、簡潔的測試代碼(Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API)。
為何使用Mock
使用Mock的目的主要有以下兩點(diǎn):
- 驗證這個對象的某些方法的調(diào)用情況滚婉,調(diào)用了多少次图筹,參數(shù)是什么等等
- 指定這個對象的某些方法的行為,返回特定的值让腹,或者是執(zhí)行特定的動作
添加gradle依賴
dependencies {
testCompile 'junit:junit:4.12'
// 如果要使用Mockito婿斥,你需要添加此條依賴庫
testCompile 'org.mockito:mockito-core:2.19.0'
// 如果你要使用Mockito 用于 Android instrumentation tests,那么需要你添加以下三條依賴庫
androidTestCompile 'org.mockito:mockito-core:2.19.0'
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
}
驗證行為
驗證方法是否被調(diào)用過|被調(diào)用的次數(shù)|至少x次|最多x次|從未被調(diào)用
import static org.mockito.Mockito.*;
//create mock
List mockedList = mock(List.class);
//use mock object
mockedList.add("one");
mockedList.clear();
//驗證add方法是否在前面被調(diào)用了一次哨鸭,且參數(shù)為“one”民宿。clear方法同樣。
verify(mockedList).add("one");
verify(mockedList).clear();
//下面的驗證會失敗像鸡。因為沒有調(diào)用過add("two")活鹰。
verify(mockedList).add("two");
======分割線======
//是否add("twice")被調(diào)用了兩次。
verify(mockedList, times(2)).add("twice");
//驗證add("twice")被調(diào)用了至少一次只估。以及其他志群。
verify(mockedList, atLeastOnce()).add("twice");
verify(mockedList, atLeast(2)).add("twice");
verify(mockedList, atMost(5)).add("twice");
verify(mockedList, never()).add("twice");
插樁(Stubbing)
使mock對象的方法返回期望值。
//stubbing蛔钙。當(dāng)get(0)被調(diào)用時锌云,返回"first". 方法get(1)被調(diào)用時,拋異常吁脱。
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
重復(fù)Stub
//重復(fù)stub桑涎,以最后一次為準(zhǔn),如下將返回"second":
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(0)).thenReturn("second");
//如下表示第一次調(diào)用時返回“first”兼贡,第二次調(diào)用時返回“second”攻冷。可以寫n個遍希。
when(mockedList.get(0)).thenReturn("first").thenReturn("second");
//如果實(shí)際調(diào)用的次數(shù)超過了stub過的次數(shù)等曼,則返回最后一次stub的值。
//例如第三次調(diào)用get(0)時,則會返回"second".
//第一次調(diào)用:拋出運(yùn)行時異常禁谦,第二次調(diào)用返回"foo"
when(mockedList.get(anyInt())).thenThrow(new RuntimeException()).thenReturn("foo");
//順序返回
when(mockedList.get(anyInt())).thenReturn("one", "two", "three");
參數(shù)匹配器
讓打樁更具靈活性胁黑,比如anyInt()將匹配所有的int值,有許多的arguments matcher州泊,參考More ArgumentMatchers
when(mockedlist.get(anyInt())).thenReturn(null);
拋出異常
doThrow(new RuntimeException()).when(mockedList).clear();
Robolectric
Robolectric is a framework that brings fast and reliable unit tests to Android. Tests run inside the JVM on your workstation in seconds.
Espresso
Google官方Instrumentation UI測試框架
未完待續(xù)...