到目前為止,我們在Android已經寫了這幾種測試。
Unit 測試:在JVM上執行的單元測試。
Instrumented 測試:與Android framework相依,需在模擬器或實機上執行測試。
UI 測試:一樣要在模擬器或實機上測試,UI測試更注重使用者的互動。
Instrumented 測試與UI測試都需要在模擬器或實機上執行,執行起來很花時間。這篇要介紹的是Robolectric,讓可以讓我們用單元測試的方式來執行Android Tests,也就是可以在JVM上執行測試,大大的提升了執行測試的效率。
延續上一個註冊的範例。這次我們要用Robolectric單元測試的方式來驗證跟View有關的:
Production code 如下
if (!isLoginIdOK) {
// 註冊失敗,資料填寫錯誤
val builder = AlertDialog.Builder(this)
builder.setMessage("帳號至少要6碼,第1碼為英文").setTitle("錯誤")
builder.show()
} else if (!isPwdOK) {
val builder = AlertDialog.Builder(this)
builder.setMessage("密碼至少要8碼,第1碼為英文,並包含1碼數字").setTitle("錯誤")
builder.show()
} else {
//註冊成功,儲存Id
Repository(this).saveUserId(loginId)
val intent = Intent(this, ResultActivity::class.java)
intent.putExtra("ID", loginId)
startActivity(intent)
}
我們將加上這幾個測試。
buide.gralde
加上testOptions.unitTests.includeAndroidResources = true
android{
testOptions.unitTests.includeAndroidResources = true
}
加上robolectric 元件
dependencies {
testImplementation "org.robolectric:robolectric:4.3"
}
新增MainActivityTest
,因為是在JVM執行,測試程式要放在test
的目錄,而不是androidTest
。
在測試程式的setup
使用Robolectric來初始化MainActivity
private lateinit var activity: MainActivity
@Before
fun setupActivity() {
MockitoAnnotations.initMocks(this)
activity = Robolectric.buildActivity(MainActivity::class.java).setup().get()
}
新增一個測試,驗證註冊成功是否有開啟ResultActivity
@Test
fun registerSuccessShouldDirectToResult() {
}
要知道有沒有使用startActivity開啟指定的Activity,需要建立一個ShadowActivity
,將用來觀察是否有開啟別的Activity
@Test
fun registerSuccessShouldDirectToResult() {
val shadowActivity = Shadows.shadowOf(activity)
}
在loginId, password 輸入欄位,放入可以通過註冊驗證的值,確保待會執行測試會是走註冊成功的流程
@Test
fun registerSuccessShouldDirectToResult() {
//arrange
val shadowActivity = Shadows.shadowOf(activity)
val userId = "A123456789"
val userPassword = "a123456789"
activity.loginId.setText(userId)
activity.password.setText(userPassword)
}
點下註冊按鈕,驗證是否有開啟ResultActivity,要開啟一個Activity需要使用Intent。所以這裡要驗證的重點其實是Intent的資料是否正確。
1.Intent的目的地的Activity
2.Intent的size
3.Intent傳送的key與value
@Test
fun registerSuccessShouldDirectToResult() {
…
//點下註冊按鈕
activity.send.performClick()
//驗證註冊成功時,是否有開啟ResultActivity
val nextIntent = shadowActivity.nextStartedActivity
assertEquals(nextIntent.component!!.className, ResultActivity::class.java.name)
assertEquals(1, nextIntent.extras!!.size())
assertEquals(userId, nextIntent.extras!!.getString("ID"))
}
要測試註冊失敗有沒有跳出Alert時,可以使用 ShadowAlertDialog.getLatestDialog()
來進行驗證。
@Test
fun registerFailShouldAlert() {
//arrange
val userId = "A1234"
val userPassword = "a123456789"
activity.loginId.setText(userId)
activity.password.setText(userPassword)
//點下註冊按鈕
activity.send.performClick()
val dialog = ShadowAlertDialog.getLatestDialog()
//Assert
assertNotNull(dialog)
assertTrue(dialog.isShowing)
}
Robolectric讓我們像單元測試一樣的測試與Android UI元件的互動。
我們曾提過一個好的單元測試應具備**獨立(Independent)**的特性。這裡的測試存在一個問題,當send.setOnClickListener
被觸發時,會呼叫RegisterVerify().isLoginIdVerify
檢查帳號是否正確,那如果這個功能壞掉了呢?那是不是在這個測試我想驗證的註冊成功是否startActivity就沒被驗證到了。
這裡給大家一個練習,讓大家可以先想看看怎麼解決。我們在介紹MVP、MVVM時,再來說明怎麼把View的處理再獨立出來。解決這個問題。
範例下載:
https://github.com/evanchen76/RobolectricSample
小技巧
Shift + command + ; 列出最近執行的測試