成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

編寫(xiě)你的第一個(gè) Android 單元測(cè)試

番茄西紅柿 / 3635人閱讀

摘要:什么是單元測(cè)試單元測(cè)試是對(duì)程序的最小單元進(jìn)行正確性檢驗(yàn)的測(cè)試工作。編寫(xiě)第一個(gè)單元測(cè)試單元測(cè)試主要使用是測(cè)試框架類(lèi)庫(kù)的擴(kuò)展庫(kù),需要在中聲明測(cè)試依賴(lài)。目標(biāo)代碼這里以一個(gè)簡(jiǎn)單的中的例子來(lái)說(shuō)明如何寫(xiě)單元測(cè)試。

TL;DR: 本文主要面向單元測(cè)試新手,首先簡(jiǎn)單介紹了什么是單元測(cè)試,為什么要寫(xiě)單元測(cè)試,討論了一下 Android 項(xiàng)目中哪些代碼適合做單元測(cè)試,并以一個(gè)簡(jiǎn)單例子演示了如何編寫(xiě)屬于你的第一個(gè) Android 單元測(cè)試(kotlin 代碼)。

什么是單元測(cè)試

單元測(cè)試是對(duì)程序的最小單元進(jìn)行正確性檢驗(yàn)的測(cè)試工作。程序單元是應(yīng)用的最小可測(cè)試部件。一個(gè)單元可能是單個(gè)程序、類(lèi)、對(duì)象、方法等。 —— Wikipedia

為什么要做單元測(cè)試

沒(méi)有測(cè)試的代碼都是不可靠的?!?魯迅

驗(yàn)證代碼正確性,增強(qiáng)對(duì)代碼的信心

最直接的好處。在沒(méi)有單元測(cè)試的時(shí)候,通常我們自測(cè)的方法就是跑一跑程序,簡(jiǎn)單構(gòu)造一下主要的分支場(chǎng)景,如果通過(guò)了,就認(rèn)為 OK 可以提交給 QA 同學(xué)了。但實(shí)際上有些時(shí)候有些分支自己是無(wú)法測(cè)到或者很難構(gòu)造出來(lái)?xiàng)l件的,這只能依靠 QA 同學(xué)手工測(cè)試來(lái)覆蓋,如果他們也沒(méi)有測(cè)到,那只能老天保佑了。而通過(guò)單元測(cè)試我們可以方便構(gòu)造各種測(cè)試場(chǎng)景,對(duì)于通過(guò)測(cè)試的代碼,我們會(huì)更有信心

在不需要 QA 參與的情況下保持或改進(jìn)產(chǎn)品質(zhì)量

說(shuō)白了就是可以放心的重構(gòu)。QA 同學(xué)總是談重構(gòu)而色變,我們?cè)谥貥?gòu)遺留代碼的時(shí)候也是提心吊膽,生怕改錯(cuò)了舊的邏輯,或者意外影響到別的模塊。有了單元測(cè)試,我們就可以更加大膽的進(jìn)行重構(gòu),重構(gòu)完只要跑一下單測(cè)驗(yàn)證是否通過(guò)就可以了(適合小范圍的重構(gòu),大的重構(gòu)可能就需要重寫(xiě)單元測(cè)試了)

加深對(duì)業(yè)務(wù)理解

在設(shè)計(jì)測(cè)試用例的過(guò)程中,需要考慮到業(yè)務(wù)上的各種場(chǎng)景,有助于我們跳出代碼加深對(duì)業(yè)務(wù)的理解

幫你寫(xiě)出更好的代碼

單元測(cè)試要求被測(cè)試的代碼高內(nèi)聚,低耦合,所以你在寫(xiě)業(yè)務(wù)代碼的時(shí)候就要考慮到如何寫(xiě)測(cè)試,或者反過(guò)來(lái),先寫(xiě)測(cè)試用例的話(huà)會(huì)讓你能夠?qū)懗鰜?lái)結(jié)構(gòu)性更好的代碼

單元測(cè)試有什么代價(jià)嗎?當(dāng)然也是有的,編寫(xiě)和維護(hù)測(cè)試用例需要花費(fèi)一定的時(shí)間和精力,當(dāng)項(xiàng)目進(jìn)度壓力比較大的時(shí)候,很多人是不愿意再花時(shí)間去寫(xiě)測(cè)試的。這就需要進(jìn)行權(quán)衡,要么不寫(xiě)然后喪失前面說(shuō)的各種好處,要么后面有時(shí)間再補(bǔ)上來(lái),但也錯(cuò)過(guò)了寫(xiě)測(cè)試的最好時(shí)間。

Android 單元測(cè)試

Android 項(xiàng)目默認(rèn)會(huì)創(chuàng)建兩個(gè)測(cè)試目錄,分別為 src/test 和 src/androidTest 前者是單元測(cè)試目錄,后者是依賴(lài) Android 框架的 instrumentation 測(cè)試目錄。聲明測(cè)試也有區(qū)別,前者是 testImplementation 后者是 androidTestImplementation,我們今天討論的是前者,也叫 Local Unit Test,意思也就是說(shuō)不依賴(lài) Android 真機(jī)或者模擬器,可以直接在本地 JVM 上運(yùn)行的單元測(cè)試。

Android 的單元測(cè)試與普通的 java 項(xiàng)目并沒(méi)有太大差異,首先需要關(guān)注的是如何分辨那些類(lèi)或者方法需要測(cè)試。

一個(gè)好的單元測(cè)試的一個(gè)重要特性就是運(yùn)行速度要快,通常是毫秒級(jí)的,而依賴(lài) Android 框架的代碼都需要在模擬器上或者真機(jī)上運(yùn)行(也不是絕對(duì)的),速度不可避免的會(huì)慢很多,所以我們?cè)谧?Android 單元測(cè)試的時(shí)候會(huì)避免讓被測(cè)試代碼對(duì) Android 框架有任何依賴(lài)。在這個(gè)條件下,一般適合進(jìn)行單元測(cè)試的代碼就是:

    MVP 結(jié)構(gòu)中的 Presenter 或者 MVVM 結(jié)構(gòu)中的 ViewModel

    Helper 或者 Utils 工具類(lèi)

    公共基礎(chǔ)模塊,比如網(wǎng)絡(luò)庫(kù)、數(shù)據(jù)庫(kù)等

如果你的項(xiàng)目中代碼與 Android 框架耦合比較高,那么可能就不得不先對(duì)目標(biāo)代碼進(jìn)行重構(gòu),然后再編寫(xiě)測(cè)試代碼。如何重構(gòu)不在本文討論范圍,請(qǐng)自行探索。

編寫(xiě)第一個(gè) Android 單元測(cè)試 SETUP

Android 單元測(cè)試主要使用是 JUnit 測(cè)試框架 + Mockito Mock 類(lèi)庫(kù) + Mockito-kotlin 的擴(kuò)展庫(kù),需要在 build.gradle 中聲明測(cè)試依賴(lài)。后面的示例代碼對(duì)應(yīng)的依賴(lài)如下。

testImplementation junit:junit:4.12
testImplementation org.mockito:mockito-core:2.19.0
testImplementation com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0

具體每個(gè)庫(kù)是用來(lái)做什么的,后面根據(jù)具體的代碼來(lái)說(shuō)明。

目標(biāo)代碼

這里以一個(gè)簡(jiǎn)單的 MVP 中 Presenter 的例子來(lái)說(shuō)明如何寫(xiě)單元測(cè)試。 以下測(cè)試代碼來(lái)自于這里,是一個(gè)食譜搜索結(jié)果展示頁(yè)面。

class SearchResultsPresenter(private val repository: RecipeRepository) :
    BasePresenter() {
  private var recipes: List);null

  fun search(query: String) {
    view);object : RecipeRepository.RepositoryCallback> {
      override fun onSuccess(recipes: List<Recipe>); {
        this@SearchResultsPresenter.recipes = recipes
        if (recipes != null && recipes.isNotEmpty()) {
          view);else {
          view);override fun onError() {
        view);fun addFavorite(recipe: Recipe) {
    recipe.isFavorited = true

    repository.addFavorite(recipe)

    val recipeIndex = recipes);if (recipeIndex != null) {
      view);fun removeFavorite(recipe: Recipe) {
    repository.removeFavorite(recipe)
    recipe.isFavorited = false
    val recipeIndex = recipes);if (recipeIndex != null) {
      view);interface View {
    fun showLoading()
    fun showRecipes(recipes: List<Recipe>)
    fun showEmptyRecipes()
    fun showError()
    fun refreshFavoriteStatus(recipeIndex: Int)
  }
}

簡(jiǎn)單分析一下代碼。

首先這個(gè) Presenter 類(lèi)包含了一個(gè)內(nèi)部類(lèi) View ,定義了 MVP 中 View 應(yīng)該實(shí)現(xiàn)的一些方法,包括顯示加載狀態(tài),顯示食譜列表,顯示空頁(yè)面,顯示錯(cuò)誤頁(yè)面,刷新最?lèi)?ài)等接口方法。

它的構(gòu)造函數(shù)接受了一個(gè) RecipeRepository 對(duì)象,我們來(lái)看一下 RecipeRepository 的定義。

interface RecipeRepository {
  fun addFavorite(item: Recipe)
  fun removeFavorite(item: Recipe)
  fun getFavoriteRecipes(): List
  fun getRecipes(query: String, callback: RepositoryCallback<List<Recipe>>)
}

interface RepositoryCallback<in T> {
  fun onSuccess(t: T);
  fun onError()
}

可以看到它是也是一個(gè)接口類(lèi),顧名思義它是一個(gè) recipe 的數(shù)據(jù)倉(cāng)庫(kù),定義了一系列的數(shù)據(jù)獲取和更新接口,至于從哪里獲取并不需要我們不關(guān)心,可以是本地文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)等等。這也正是依賴(lài)翻轉(zhuǎn)原則的體現(xiàn)。

這個(gè) Presenter 又繼承了 BasePresenter,這個(gè)類(lèi)是一個(gè)抽象類(lèi),定義了兩個(gè)方法,分別是 attachView() 和 detachView(),還有一個(gè)字段 view。

abstract class BasePresenter<V> {
  protected var view: V);null

  fun attachView(view: V) {
    this.view = view
  }

  fun detachView() {
    this.view = null
  }
}

回到 SearchResultsPresenter 自身,這個(gè)類(lèi)有三個(gè)主要方法,第一個(gè) search() 接受一個(gè)字符串,調(diào)用了 repository 的方法獲取搜索結(jié)果,根據(jù)結(jié)果分別調(diào)用 View 的不同方法;第二個(gè) addFavorite(),它接受一個(gè) recipe 對(duì)象,將其設(shè)置為最?lèi)?ài),并調(diào)用 repository 更新到數(shù)據(jù)倉(cāng)庫(kù)中,最后調(diào)用 view 方法刷新 UI;第三個(gè)方法 removeFavorite() ,它與上一個(gè)方法剛好相反。基類(lèi)的方法不在我們測(cè)試范圍內(nèi),不用考慮。

這三個(gè)方法無(wú)疑就是我們單元測(cè)試的目標(biāo)了,繼續(xù)看如何寫(xiě)測(cè)試代碼。

創(chuàng)建測(cè)試類(lèi)

首先定位到我們要測(cè)試的類(lèi),使用快捷鍵 CMD + N (Generate),選中 Test,就會(huì)出來(lái)一個(gè)彈窗,引導(dǎo)我們創(chuàng)建一個(gè)對(duì)應(yīng)的測(cè)試類(lèi),類(lèi)名通常是我們要測(cè)試的類(lèi) + Test 后綴。要記得位置要放到 src/test 目錄下喲(也可以手動(dòng)定位到相應(yīng)目錄,創(chuàng)建一個(gè)新的文件,但會(huì)慢很多)。

編寫(xiě)測(cè)試代碼

行為驗(yàn)證

首先添加如下代碼

class SearchResultsPresenterTests {

  private lateinit var repository: RecipeRepository
  private lateinit var presenter: SearchResultsPresenter
  private lateinit var view: SearchResultsPresenter.View

  @Before
  fun setup() {
    repository = mock()
    view = mock()
    presenter = SearchResultsPresenter(repository)
    presenter.attachView(view)
  }

解釋一下,這里可能比較陌生的代碼有兩處:

    @Before 注解

這個(gè)注解是 Junit 測(cè)試框架的一部分,當(dāng)前測(cè)試類(lèi)中的每一個(gè)測(cè)試用例都會(huì)先調(diào)用 @Before 注解的方法,所以可以用來(lái)做一些公共的 setup 的操作。具體在這里,我們要測(cè)試的是 Presenter,所以就是創(chuàng)建好了一個(gè) Presenter 實(shí)例,并配置了需要與 Presenter 交互的 View / Repository 等外部對(duì)象。與 Before 對(duì)應(yīng),還有一個(gè) @After 注解,可以標(biāo)注一個(gè)方法,用來(lái)在每個(gè)用例執(zhí)行完畢后做一些清理操作,如果不需要的話(huà) ,也可以省略不寫(xiě)。

    mock() 方法

這個(gè)方法是 mockito-kotlin 庫(kù)提供的,它是一個(gè)包裝類(lèi)庫(kù),背后又調(diào)用了 Mockito 類(lèi)庫(kù),這個(gè)庫(kù)可以用來(lái)偽造一些穩(wěn)定的依賴(lài)類(lèi),避免不穩(wěn)定的依賴(lài)造成我們的單元測(cè)試結(jié)果不可預(yù)期。具體在這里,因?yàn)槲覀儨y(cè)試的目標(biāo)是 Presenter 類(lèi),與 Presenter 有交互關(guān)系的 View 和 Repo 都有抽象的接口,我們不想測(cè)試具體的 View 和 Repo 類(lèi)(一 View 依賴(lài)了 Android 框架,運(yùn)行太慢,二 Repo 可能依賴(lài)了網(wǎng)絡(luò)或者數(shù)據(jù)庫(kù)或者文件,不夠穩(wěn)定),就可以使用 mock() 方法來(lái)創(chuàng)建一個(gè)模擬的類(lèi)(這里 mock() 是一個(gè)泛型方法,使用了 kotlin 的類(lèi)型推斷特性)。 Mock 出來(lái)的類(lèi)可以用來(lái)檢測(cè)對(duì)應(yīng)的方法是否被調(diào)用,調(diào)用了多少次,調(diào)用的次序等等。

接下來(lái)添加第一個(gè)測(cè)試用例,我們要驗(yàn)證一下調(diào)用 presenter 的 search() 方法后,View 的 showLoading() 方法會(huì)被調(diào)用到。

@Test
fun search_callsShowLoading() {
    presenter.search("eggs")
    verify(view).showLoading()
}

首先當(dāng)然是先調(diào)用 presenter 的 search 方法,然后我們 調(diào)用了一個(gè) verify 方法,它會(huì)接受一個(gè) Mock 的對(duì)象,然后我們就可以驗(yàn)證這個(gè) Mock 對(duì)象的 showLoading() 方法被調(diào)用過(guò)了! 很簡(jiǎn)單有沒(méi)有。在這個(gè)方法聲明的左邊,有一個(gè)運(yùn)行按鈕,點(diǎn)擊就可以執(zhí)行這個(gè)測(cè)試用例了(快捷鍵 Ctrl + Shift + R)。

我們?cè)賮?lái)寫(xiě)一個(gè)比較復(fù)雜的測(cè)試用例,這次我們要驗(yàn)證一下 search() 調(diào)用后,repo 的 getRecipes() 方法會(huì)調(diào)用到,當(dāng)回調(diào)返回后,view 的 showRecipes() 方法會(huì)調(diào)用到。

@Test
fun search_succeed_callShowRecipes() {
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
    val recipes = listOf(recipe)
    doAnswer {
        val callback: RepositoryCallback> = it.getArgument(1)
        callback.onSuccess(recipes)
    }.whenever(repository).getRecipes(eq("eggs"), any())

    presenter.search("eggs")

    verify(repository).getRecipes(eq("eggs"), any())
    verify(view).showRecipes(eq(recipes))
}

喔,這個(gè)方法代碼量一下多了好多,但不要被嚇到,其實(shí)都很好理解,首先我們創(chuàng)建了 recipes 對(duì)象來(lái)作為 repo 的搜索的返回結(jié)果,這里我們使用了一個(gè)新的方法,doAnswer{}.whenever().getRecipes(),也很好理解,就是當(dāng)調(diào)用的到 Mock 對(duì)象的 getRecipes() 方法的時(shí)候做一些事情,在 doAnswer{} 方法體中,我們拿到了回調(diào)的對(duì)象,并執(zhí)行了 onSuccess() 回調(diào),將我們構(gòu)造的搜索結(jié)果返回回去(這個(gè)過(guò)程就叫做 Stubbing,翻譯過(guò)來(lái)就是插樁)。好了,到這里位置我們已經(jīng)構(gòu)造好了測(cè)試的前提條件,下一步就是調(diào)用 presenter 的 search() 方法了。最后就是驗(yàn)證步驟了,也很好理解,不廢話(huà)了。

前面有兩個(gè)方法沒(méi)有講到,就是 eq("eggs") 方法和 any() 方法,這兩個(gè)方法返回都是 Matcher 對(duì)象,顧名思義就是用來(lái)校驗(yàn)參數(shù)是否與預(yù)期的符合,any() 比較特殊,意思就是我們不在乎到底是什么。需要注意的是,如果在方法調(diào)用時(shí)有一個(gè)參數(shù)使用了 Matcher,所有其他參數(shù)都必須也是 Matcher,這個(gè)不需要自己記住,如果你寫(xiě)錯(cuò)了,運(yùn)行時(shí)就會(huì)報(bào)錯(cuò)。

根據(jù)前面的例子,很容易就可以聯(lián)想到還可以增加 search 失敗的時(shí)候調(diào)用 view.showError(),以及 search 結(jié)果為空時(shí),調(diào)用 view.showEmpty() 的測(cè)試用例,小菜一疊是不是?

前面寫(xiě)的這些測(cè)試用例都是驗(yàn)證被測(cè)試對(duì)象依賴(lài)的模塊的某些方法可以被正確調(diào)用,所以可以歸為一類(lèi)叫做行為驗(yàn)證,也就是 Mockito 通常被用來(lái)做的事情。

狀態(tài)驗(yàn)證

還有一類(lèi)測(cè)試,叫做狀態(tài)驗(yàn)證,通常使用 JUnit 庫(kù)中的 Assert 函數(shù),我們也舉一個(gè)例子。presenter 中有一個(gè)方法 addFavorite() 是將一個(gè)食譜添加為最?lèi)?ài),我們來(lái)看看應(yīng)該怎么寫(xiě)測(cè)試用例。

@Test
fun addFavorite_shouldUpdateRecipeStatus() {
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
    presenter.addFavorite(recipe)
    assertThat(recipe.isFavorited, `is`(true))
}

還是很簡(jiǎn)單,我們構(gòu)造了一個(gè)默認(rèn) favorited 屬性為 false 的 recipe,然后調(diào)用 addFavorite() 方法,然后去驗(yàn)證 recipe 對(duì)象的 isFavorited 屬性應(yīng)該是 True . 這里驗(yàn)證的時(shí)候使用了 JUnit 庫(kù)中的 assertThat() 方法,這個(gè)方法接收兩個(gè)參數(shù) ,第一個(gè)參數(shù)是驗(yàn)證的目標(biāo),第二個(gè)參數(shù)是一個(gè) Matcher,因?yàn)?kotlin 中 is 是保留關(guān)鍵字,所以需要用 ` 進(jìn)行轉(zhuǎn)義。

相似的,也可以給 presenter 的 removeFavorite() 方法添加測(cè)試用例。

完整的測(cè)試類(lèi)

好了,現(xiàn)在我們可以給 Presenter 編寫(xiě)出一個(gè)完整的測(cè)試類(lèi)了,看一下完整的代碼。

class SearchResultsPresenterTests {

    private lateinit var repository: RecipeRepository
    private lateinit var presenter: SearchResultsPresenter
    private lateinit var view: SearchResultsPresenter.View

    @Before
    fun setup() {
        repository = mock()
        view = mock()
        presenter = SearchResultsPresenter(repository)
        presenter.attachView(view)
    }

    @Test
    fun search_callsShowLoading() {
        presenter.search("eggs")
        verify(view).showLoading()
    }

    @Test
    fun search_succeed_callShowRecipes() {
        val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
        val recipes = listOf(recipe)

        doAnswer {
            val callback: RepositoryCallback> = it.getArgument(1)
            callback.onSuccess(recipes)
        }.whenever(repository).getRecipes(eq("eggs"), any())

        presenter.search("eggs")

        verify(repository).getRecipes(eq("eggs"), any())
        verify(view).showRecipes(eq(recipes))
    }

    @Test
    fun search_error_callShowError() {
        doAnswer {
            val callback: RepositoryCallback> = it.getArgument(1)
            callback.onError()
        }.whenever(repository).getRecipes(eq("eggs"), any())

        presenter.search("eggs")

        verify(repository).getRecipes(eq("eggs"), any())
        verify(view).showError()
    }

    @Test
    fun addFavorite_shouldUpdateRecipeStatus() {
        val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
        presenter.addFavorite(recipe)
        assertThat(recipe.isFavorited, `is`(true))
    }

    @Test
    fun removeFavorite_shouldUpdateRecipeStatus() {
        val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", true)
        presenter.removeFavorite(recipe)
        assertThat(recipe.isFavorited, `is`(false))
    }
}

這已經(jīng)是一個(gè)相對(duì)完整的測(cè)試類(lèi)了,在類(lèi)聲明的第一行的左邊,同樣有一個(gè)按鈕點(diǎn)擊后可以運(yùn)行整個(gè)類(lèi)內(nèi)定義的所有測(cè)試用例,同樣也有快捷鍵 Ctrl + Shift + R,光標(biāo)放到類(lèi)上運(yùn)行即可。執(zhí)行結(jié)果如下圖。

如何判斷測(cè)試的有效性

測(cè)試代碼很快寫(xiě)完了,你可能會(huì)想,怎么才能衡量測(cè)試的有效性呢?這里就要引入另外一個(gè)概念,叫測(cè)試覆蓋率 (Code Coverage)。

測(cè)試覆蓋率有著不同的維度,比如類(lèi)數(shù)量、方法數(shù)量、行數(shù)、條件分支等等,具體什么意思不在本文討論范圍,大家可以自行探索。Android Studio 內(nèi)置了工具可以幫我們進(jìn)行統(tǒng)計(jì)。

回顧前面運(yùn)行測(cè)試用例的時(shí)候,Android Studio 會(huì)幫我們創(chuàng)建一個(gè) Task,而在運(yùn)行按鈕右邊,還有一個(gè)按鈕叫 “Run [test-task-name] with coverage”,這個(gè)就是 IDE 內(nèi)置的統(tǒng)計(jì)測(cè)試覆蓋率的工具啦。

運(yùn)行之后會(huì)自動(dòng)打開(kāi)一個(gè) Coverage 結(jié)果頁(yè)面窗口,點(diǎn)進(jìn)去就可看到當(dāng)前測(cè)試 task 對(duì)相關(guān)的被測(cè)試代碼的一個(gè)覆蓋情況。結(jié)果顯示我們的測(cè)試用例覆蓋了 100% 的類(lèi)和方法和 88% 的行數(shù)。

點(diǎn)擊打開(kāi)具體類(lèi)還能看到每一行代碼有沒(méi)有執(zhí)行到,非常好用,為我們對(duì)測(cè)試用例的調(diào)整和完善提供了很好的參考價(jià)值。比如,觀察這個(gè) addFavorite() 方法,我們的測(cè)試用例沒(méi)有覆蓋到 view 的 refresh 方法調(diào)用情況。

陷阱注意!

看起來(lái)測(cè)試覆蓋率是一個(gè)很好的衡量單元測(cè)試覆蓋程度甚至是質(zhì)量的指標(biāo)注意,實(shí)際上確實(shí)有很多人也因此會(huì)追求 100% 的測(cè)試覆蓋率,但這樣真的好嗎?

“單元測(cè)試并不是越多越好,而是越有效越好。”這句話(huà)不是我說(shuō)的,而是 Kent Beck 說(shuō)的,他是 TDD 和 XP 的發(fā)起者,也是敏捷開(kāi)發(fā)的奠基人。說(shuō)這些的意思是提醒大家不要陷入教條主義,測(cè)試的目的是為了提升對(duì)代碼質(zhì)量,只要自己和團(tuán)隊(duì)有信心,就愛(ài)怎么測(cè)試就怎么測(cè),怎么合適怎么測(cè),沒(méi)有必要一定要寫(xiě)測(cè)試,一定要測(cè)試先行。

延伸閱讀

OK,到此為止,你應(yīng)該已經(jīng)學(xué)會(huì)了編寫(xiě) Android 單元測(cè)試的基本知識(shí),如果想進(jìn)一步了解 Android 測(cè)試,建議可以閱讀以下資料:

如果相對(duì) Android 測(cè)試有系統(tǒng)的了解,可以參考 Google 官方文檔developer.android.com | testing fundamentals

如果你想理解 Android UI 測(cè)試,可以參考 Google 在 19 年 IO 上剛剛更新的 codeLab Android Testing Codelab

查看 Mockito-Kotlin 和 Mockito 的 wiki 來(lái)了解更多與 Mock 相關(guān)的知識(shí)

CoolShell 上有一篇文章討論了 “單元測(cè)試要做多細(xì)?” | 酷 殼 - CoolShell

Happy unit testing!

參考

android unit testing with mockito | raywenderlich.com

“單元測(cè)試要做多細(xì)?” | 酷 殼 - CoolShell

The Way of Testivus - Unit Testing Wisdom From An Ancient Software Start-up

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7051.html

相關(guān)文章

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<