本機測試會直接在您的工作站執行,而非 Android 裝置或模擬器因此,它會使用您的本機 Java 虛擬機器 (JVM), 執行測試,而非 Android 裝置本機測試可讓您更快速地評估應用程式邏輯。不過,無法與 Android 架構互動會限制您執行的測試類型。
單元測試會驗證一小段程式碼 ( 測試。方法是執行該程式碼並檢查結果。
單元測試通常很簡單,但如果測試中的單元並未以可測試性為設計考量,則其設定可能會出現問題:
- 您要驗證的程式碼必須可供測試存取。適用對象 例如,您無法直接測試私人方法。相反地 透過公用 API 存取
- 如要在「隔離」中執行單元測試,請確認單元的依附元件。 測試中的元件必須替換為您控制的元件,例如 其他的測試替身。如果程式碼依賴 Android 架構,這就特別成問題。
如要瞭解 Android 中常見的單元測試策略,請參閱「要測試的項目」。
本機測試位置
根據預設,本機單元測試的原始檔案會放在 module-name/src/test/
中。使用 Android Studio 建立新專案時,這個目錄就會存在。
新增測試依附元件
您還需要為專案設定測試依附元件,以便使用 JUnit 測試架構提供的標準 API。
如要這麼做,請開啟應用程式模組的 build.gradle
檔案,然後指定以下項目
做為依附元件使用 testImplementation
函式,表示這些依附元件適用於本機測試來源集,而非應用程式:
dependencies {
// Required -- JUnit 4 framework
testImplementation "junit:junit:$jUnitVersion"
// Optional -- Robolectric environment
testImplementation "androidx.test:core:$androidXTestVersion"
// Optional -- Mockito framework
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Optional -- mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
// Optional -- Mockk framework
testImplementation "io.mockk:mockk:$mockkVersion"
}
建立本機單元測試類別
本機單元測試類別可用來編寫為 JUnit 4 測試類別。
如要這麼做,請建立包含一或多個測試方法的類別,通常會在 module-name/src/test/
中建立。測試方法以 @Test
註解開頭,並包含要測試的元件單一面向的程式碼,用於執行及驗證該元件。
以下範例說明如何實作本機單元測試類別。
嘗試驗證的emailValidator_correctEmailSimple_returnsTrue()
測試方法
isValidEmail()
:這是應用程式內的方法。測試函式會傳回
如果isValidEmail()
也傳回 true,則為 true。
Kotlin
import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")) } }
Java
import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")); } }
您應建立易讀的測試,評估應用程式中的元件是否傳回預期結果。建議您使用斷言程式庫,例如 junit.Assert、Hamcrest 或 Truth。上方的程式碼片段是如何使用 junit.Assert
的範例。
可模擬的 Android 程式庫
執行本機單元測試時,Android Gradle 外掛程式會包含 內含 Android 架構的所有 API 的程式庫, 專案中使用的版本這個程式庫會保留這些 API 的所有公開方法和類別,但方法內的程式碼已遭移除。如果有任何 方法存取時,此測試會擲回例外狀況。
這可讓您在參照 Android 中的類別時建立本機測試
例如 Context
更重要的是,您可以使用模擬
與 Android 類別聯手
模擬 Android 依附元件
常見的問題是找出類別正在使用字串資源。你可以
呼叫 Context
中的 getString()
方法,即可取得字串資源
類別不過,本機測試無法使用 Context
或任何 Context
方法,因為這些屬於 Android 架構。理想情況下,呼叫 getString()
會從類別中移出,但這並不總是可行。解決方法是
建立 Context
的模擬或虛設常式,且其會在其下列情況傳回相同的值
叫用 getString()
方法。
使用可模擬的 Android 程式庫和模擬架構,例如 Mockito 或 MockK,即可將 在單元測試中,Android 類別模擬的行為。
如要使用 Mockito 在本機單元測試中新增模擬物件,請按照下列步驟操作: 程式設計模型
- 如「設定測試環境」一節所述,請在
build.gradle
檔案中加入 Mockito 程式庫依附元件。 - 在單元測試類別定義的開頭,將
@RunWith(MockitoJUnitRunner.class)
註解。這項註解會告知 Mockito 測試執行程式,驗證您對架構的使用方式是否正確,並簡化模擬物件的初始化作業。 - 如要為 Android 依附元件建立模擬物件,請在欄位宣告之前新增
@Mock
註解。 - 如要虛設依附元件的行為,您可以指定條件並
使用
when()
和thenReturn()
,符合條件時傳回的值 方法。
以下範例說明如何在 Kotlin 中使用 Mockito-Kotlin 建立的模擬 Context
物件,建立單元測試。
import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
private const val FAKE_STRING = "HELLO WORLD"
@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {
@Mock
private lateinit var mockContext: Context
@Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
val mockContext = mock<Context> {
on { getString(R.string.name_label) } doReturn FAKE_STRING
}
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getName()
// ...then the result should be the expected one.
assertEquals(result, FAKE_STRING)
}
}
如要進一步瞭解如何使用 Mockito 架構,請參閱 Mockito API
參照和 SharedPreferencesHelperTest
類別中的
程式碼範例。另外,也請嘗試使用 Android Testing Codelab。
錯誤:「方法 ... 未模擬」
如果您嘗試存取模擬 Android 程式庫的任何
傳回給 Error: "Method ... not mocked
訊息的方法。
如果擲回的例外狀況對測試造成問題,您可以更改
就會根據
傳回類型。如要這麼做,請在 Groovy 中,在專案的頂層 build.gradle
檔案中新增下列設定:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}