為了確保app或者library的質(zhì)量,自動化測試非常重要屁桑。很長一段時間,ADT缺少對自動化測試的支持栏赴,但是最近蘑斧,Google花費了很大的努力幫助開發(fā)者進行測試。一些老的框架進行了升級须眷,并添加了一些新的框架竖瘾,以確保我們可以徹底測試app和library。我們不僅可以在Android Studio中運行它們柒爸,也可以使用Gradle在命令行運行准浴。
本章,我們會探索測試Android app和library的幾種方式捎稚。我們還將研究Gradle如何幫助實現(xiàn)自動化測試乐横。
本章包含如下內(nèi)容:
- 單元測試
- 功能測試
- 測試覆蓋率
單元測試
好的單元測試不僅能保證工程質(zhì)量,也可以用來檢測新的代碼是否會破壞原有功能今野。Android Studio和Gradle Android插件對單元測試提供了本地支持葡公,你只需要進行少量配置就可以使用。
JUnit
JUnit是一個非常流行的單元測試庫条霜,已經(jīng)有了十多年的歷史催什。它使測試變得簡單易懂。需要記住的是宰睡,這些流行的單元測試僅對測試業(yè)務(wù)邏輯有用蒲凶,不能測試Android SDK相關(guān)的代碼。
在寫測試之前拆内,你需要創(chuàng)建一個目錄旋圆。按照慣例,目錄名稱為test
麸恍,和main
目錄在同一級別灵巧。目錄結(jié)構(gòu)如下:
app
└─── src
├─── main
│ ├─── java
│ │ └─── com.example.app
│ └───res
└─── test
└─── java
└───com.example.app
隨后你可以在src/test/java/com.example.app
目錄添加測試類。
為了使用JUnit的最新特性抹沪,建議使用JUnit4.0以上版本刻肄。添加如下依賴:
dependencies {
testCompile 'junit:junit:4.12'
}
注意我們此處使用testCompile
,而不是compile
融欧,這可以確保只在運行測試時構(gòu)建此依賴敏弃,正式打包時不會。
如果你的某個構(gòu)建類型或者product flavors有一些特性噪馏,你也可以為這個特定的構(gòu)建單獨添加測試依賴权她。比如虹茶,你只想為付費版添加JUnit測試,可以這樣做:
dependencies {
testPaidCompile 'junit:junit:4.12'
}
配置好這些隅要,就可以編寫測試代碼了。下面的例子用來測試一個類中兩數(shù)相加的方法:
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class LogicTest {
@Test
public void addingNegativeNumberShouldSubtract() {
Logic logic = new Logic();
assertEquals("6 + -2 must be 4", 4, logic.add(6, -2));
assertEquals("2 + -5 must be -3", -3, logic.add(2, -5));
}
}
要運行所有級別的測試董济,只需執(zhí)行gradlew
測試步清。如果你只想在某個構(gòu)建變體運行測試,只需要添加上這個變體的名字虏肾。比如廓啊,執(zhí)行gradlewtestDebug
將只在debug變體上運行測試。如果測試失敗封豪,Gradle會在命令行中打印錯誤信息谴轮。如果運行成功,Gradle會顯示BUILD SUCCESSFUL吹埠。
單行測試失敗將導(dǎo)致真?zhèn)€測試任務(wù)失敗第步。這意味著并不是所有的測試都會執(zhí)行。如果你希望這個測試單元在所有的構(gòu)建變體上都被執(zhí)行缘琅,添加continue
標(biāo)識:
$ gradlew test --continue
你也可以僅為某個構(gòu)建變體編寫測試粘都,只需要將測試類放在相應(yīng)的目錄就可以了。比如刷袍,想要測試付費版本的話翩隧,可以將測試類放在src/testPaid/java/com.example.app
中。
如果你不想運行整個測試單元呻纹,只想測試某個類堆生,可以添加標(biāo)識:
$ gradlew testDebug --tests="*.LogicTest"
執(zhí)行測試不僅會運行所有的測試,還會生成一個測試報告雷酪,路徑為app/build/reports/tests/debug/index.html
淑仆。測試報告可以幫助排查問題,在自動測試中非常有用太闺。Gradle會為每個構(gòu)建變體生成一個測試報告糯景。
你也可以在Android Studio中運行測試。在IDE中可以得到及時反饋省骂,并且可以點擊失敗的測試蟀淮,從而快速定位到問題代碼。
如果你想測試包含Android特有類或者資源引用的代碼钞澳,常規(guī)單元測試是行不通的怠惶,這會引發(fā)java.lang.RuntimeException: Stub!
錯誤。為了解決這個問題轧粟,你可以自己實現(xiàn)Android SDK的每個方法策治,或者使用一個模擬框架脓魏。幸運的是,一些庫解決了這個問題通惫,其中最流行的是Robolectric茂翔,它提供了簡單的方式來測試Android功能,并且不需要設(shè)備或者模擬器履腋。
Robolectric
使用Robolectric珊燎,你可以編寫使用了Android SDK和資源的測試,并在JVM中運行它遵湖。也就是說悔政,你不需要設(shè)備或者模擬器就可以測試UI組件。
為了使用Robolectric延旧,你需要添加一些依賴谋国。除了Robolectric,你也需要JUnit迁沫。如果你使用了Android support library芦瘾,你還需要Robolectric shadow。
apply plugin: 'org.robolectric'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
testCompile 'junit:junit:4.12'
testCompile'org.robolectric:robolectric:3.0'
testCompile'org.robolectric:shadows-support:3.0'
}
和常規(guī)單元測試一樣弯洗,Robolectric測試類需要放在src/test/java/com.example.app
目錄下旅急。不同的是,你可以在測試中使用Android類和資源牡整。比如藐吮,測試驗證一個TextView的文本在點擊按鈕后發(fā)生了變化:
@RunWith(RobolectricTestRunner.class)
@Config(manifest = "app/src/main/AndroidManifest.xml", sdk = 18)
public class MainActivityTest {
@Test
public void clickingButtonShouldChangeText() {
AppCompatActivity activity = Robolectric.buildActivity(MainActivity.class).create().get();
Button button = (Button)activity.findViewById(R.id.button);
TextView textView = (TextView)activity.findViewById(R.id.label);
button.performClick();
assertThat(textView.getText().toString(), equalTo(activity.getString(R.string.hello_robolectric)));
}
}
Robolectric在Android L系統(tǒng)和suport library上存在一些問題。如果你碰到無法找到資源和兼容庫相關(guān)的錯誤逃贝,可以嘗試在模塊中添加一個
project.properties
文件谣辞,文件包含如下內(nèi)容:android.library.reference.1=../../build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.0 android.library.reference.2=../../build/intermediates/exploded-aar/com.android.support/support-v4/22.2.0
這會幫助Robolectric找到兼容庫資源。
功能測試
功能測試用于測試應(yīng)用的幾個模塊是否如預(yù)期那樣正常工作沐扳。比如泥从,你可以創(chuàng)建一個功能測試,用于確認點擊一個按鈕是否會打開一個新的頁面沪摄。Android有幾個功能測試框架躯嫉,最簡單易用的是Espresso。
Espresso
Google創(chuàng)建了Espresso杨拐,使開發(fā)者更容易編寫功能測試祈餐。它在Android support repository中提供,你可以使用SDK Manager安裝哄陶。
為了在設(shè)備上運行測試帆阳,你需要有一個測試運行器。在testing support library中屋吨,Google提供了AndroidJUnitRunner
測試運行器蜒谤,它可以幫助你在Android設(shè)備上運行JUnit測試類山宾。測試運行器會在設(shè)備上安裝應(yīng)用APK和測試APK,運行所有測試鳍徽,然后根據(jù)結(jié)果創(chuàng)建測試報告资锰。
假如你已經(jīng)下載了testing support library,可以如下配置測試運行器:
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
在使用Espresso之前阶祭,你還需要配置幾個依賴:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
androidTestCompile 'com.android.support.test:runner:0.3'
androidTestCompile 'com.android.support.test:rules:0.3'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2'
}
你需要引用testing support library和espresso-core
才能使用Espresso台妆。最后一個依賴,espresso-contrib
胖翰,是Espresso的一些補充特性,但不是核心庫的一部分切厘。
注意這些依賴使用的是androidTestCompile
配置萨咳,而不是testCompile
。這是為了區(qū)分單元測試和功能測試疫稿。
如果這個時候嘗試運行測試培他,你會得到下面的錯誤:
Error: duplicate files during packaging of APK app-androidTest.apk
Path in archive: LICENSE.txt
Origin 1: ...\hamcrest-library-1.1.jar
Origin 2: ...\junit-dep-4.10.jar
錯誤的描述很清晰,因為一個重復(fù)的文件遗座,Gradle將不能完成構(gòu)建舀凛。幸運的是,它僅是一個許可描述途蒋,所以我們可以在構(gòu)建中移除猛遍。這個錯誤本身也包含如何修復(fù)的信息:
You can ignore those files in your build.gradle:
android {
packagingOptions {
exclude 'LICENSE.txt'
}
}
配置好構(gòu)建文件之后,你就可以添加測試了号坡。功能測試和常規(guī)的單元測試放在不同的目錄下懊烤。正如依賴配置一樣,你需要使用androidTest
代替test
宽堆,正確的功能測試目錄是src/androidTest/java/com.example.app
腌紧。下面是一個測試類的例子,用于檢測MainActivity
的TextView
中的文本是否正確畜隶。
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TestingEspressoMainActivityTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
@Test
public void testHelloWorldIsShown() {
onView(withText("Hello world!")).check(matches(isDisplayed()));
}
}
在運行Espresso測試之前壁肋,你需要確保有一個設(shè)備或者模擬器。如果你忘記連接設(shè)備籽慢,運行測試任務(wù)會引發(fā)如下異常:
Execution failed for task ':app:connectedAndroidTest'.
>com.android.builder.testing.api.DeviceException:
java.lang.RuntimeException: No connected devices!
當(dāng)你連接上設(shè)備或者啟動了模擬器之后浸遗,你可以使用gradlew connectedCheck
來運行Espresso測試。這個任務(wù)會執(zhí)行connectedAndroidTest
和createDebugCoverageReport
嗡综,前者會在所有連接的設(shè)備上為debug變體運行所有測試乙帮,后者會生成一個測試報告。
你可以在應(yīng)用的build/outputs/reports/androidTests/connected
目錄找到生成的測試報告极景。
功能測試報告會顯示運行測試的設(shè)備以及Android版本察净。你可以同時在多個設(shè)備上運行測試驾茴,所以設(shè)備信息可以很容易的查看特定設(shè)備或者特定版本的問題。
如果你想在Android Studio中得到測試的反饋氢卡,你需要設(shè)置run/debug配置锈至。run/debug配置代表一組run/debug啟動屬性。Android Studio的工具欄有一個配置選擇器译秦,你可以選擇想要使用的run/debug配置峡捡。
你可以點擊Edit Configurations…打開配置編輯器,創(chuàng)建一個新的Android測試配置筑悴。選擇測試模塊们拙,設(shè)備運行器設(shè)置為AndroidJUnitRunner
。
將新配置保存后阁吝,你就可以在配置選擇器中選擇它砚婆,然后點擊Run按鈕來運行所有測試。
在Android Studio中運行Espresso測試有一個問題:不會生成測試報告突勇。這是因為Android Studio執(zhí)行
connectedAndroidTest
任務(wù)装盯,而不是connectedCheck
任務(wù),而測試報告是由connectedCheck
生成的甲馋。
測試覆蓋率
為Android工程編寫測試后埂奈,需要知道測試在代碼庫中的覆蓋率。Java有很多測試覆蓋率工具定躏,最流行的是Jacoco账磺,它也是默認支持的,很容易上手共屈。
Jacoco
啟用覆蓋率報告非常簡單绑谣。你只需要在測試的構(gòu)建類型中設(shè)置testCoverageEnabled = true
。
buildTypes {
debug {
testCoverageEnabled = true
}
}
啟用測試覆蓋率后拗引,執(zhí)行gradlew connectedCheck
會生成覆蓋率報告借宵。真正生成報告的是createDebugCoverageReport
。即使它沒有被列入文檔矾削,執(zhí)行gradlew tasks
時也不會出現(xiàn)在任務(wù)列表中壤玫,但你可以直接運行它。而由于createCoverageReport
依賴connectedCheck
哼凯,所以你不可以單獨運行它們欲间。依賴connectedCheck
也表明你需要連接設(shè)備或者模擬器來生成測試覆蓋率報告。
執(zhí)行任務(wù)之后断部,你可以在app/build/outputs/reports/coverage/debug/index.html
路徑找到覆蓋率報告猎贴。每個構(gòu)建變體有各自的目錄。
如果你想設(shè)置一個特定版本的Jacoco,只需:
jacoco {
toolVersion = "0.7.1.201405082137"
}