精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

單元測(cè)試框架的對(duì)比

原創(chuàng) 精選
開(kāi)發(fā) 測(cè)試
在開(kāi)發(fā)過(guò)程中,我發(fā)現(xiàn) JUnit 并不總是那么好用。它在一些情況下需要耗費(fèi)挺多精力才能編寫(xiě)出讓人滿意的測(cè)試。

作者 | 高悅翔

在我們?nèi)粘5?TDD 開(kāi)發(fā)中,永遠(yuǎn)繞不過(guò)去的就是要編寫(xiě)測(cè)試。而對(duì)于一個(gè) Java 程序員,JUnit 似乎是一個(gè)不二的選擇。它的確是一個(gè)十分優(yōu)秀的工具,在大多數(shù)情況下都能夠幫助我們完成測(cè)試的工作。

但是在開(kāi)發(fā)過(guò)程中,我發(fā)現(xiàn) JUnit 并不總是那么好用。它在一些情況下需要耗費(fèi)挺多精力才能編寫(xiě)出讓人滿意的測(cè)試。

JUnit 不擅長(zhǎng)的事情

一個(gè)讓人滿意的測(cè)試,應(yīng)該能夠清晰地體現(xiàn)被測(cè)試的目標(biāo)、測(cè)試的目的以及測(cè)試的輸入輸出,并且應(yīng)遵循 DRY 原則,盡可能的減少測(cè)試中的重復(fù)內(nèi)容。

JUnit 可以通過(guò)設(shè)計(jì)測(cè)試方法名和組織方法內(nèi)的代碼的方式清晰地表達(dá)意圖,也可以通過(guò)參數(shù)化測(cè)試來(lái)減少相同測(cè)試目的的代碼重復(fù)。但是它在這些地方都做得不夠好。

清晰表達(dá)測(cè)試的目的

在使用 JUnit 時(shí),清晰的表達(dá)測(cè)試意圖并不是總能做到的事情。這主要體現(xiàn)在兩個(gè)方面。

(1) 如何命名測(cè)試方法

第一個(gè)體現(xiàn)就是在使用 Java 編寫(xiě)測(cè)試時(shí),采用什么樣的命名風(fēng)格來(lái)命名測(cè)試。是為了代碼風(fēng)格的統(tǒng)一而選擇駝峰?還是為了更高的可讀性選擇下劃線?這個(gè)問(wèn)題在不同的項(xiàng)目中有不同的實(shí)踐,看起來(lái)是沒(méi)有一個(gè)統(tǒng)一的認(rèn)識(shí)。

而這個(gè)問(wèn)題的根源是 JUnit 的測(cè)試名稱是 Java 的方法名,而 Java 的方法名又不能在其中插入空格。所以除了下面要介紹的兩種測(cè)試工具外,采用 Kotlin 來(lái)編寫(xiě) JUnit 也是一種方式。

(2) 如何組織方法內(nèi)的代碼

第二個(gè)體現(xiàn)就是 JUnit 對(duì)測(cè)試方法內(nèi)部如何編寫(xiě)沒(méi)有強(qiáng)制的規(guī)定。這就意味著我可以在測(cè)試?yán)锩嫒我獾亟M織代碼,比如在一個(gè)測(cè)試方法里面對(duì)一個(gè)方法調(diào)用多次并驗(yàn)證每一次的結(jié)果,或者把調(diào)用測(cè)試目錄的邏輯和準(zhǔn)備數(shù)據(jù)的邏輯以及驗(yàn)證邏輯混合到一起。 總之這樣的結(jié)果就是測(cè)試方法內(nèi)的代碼組織方式千奇百怪,每當(dāng)閱讀別人編寫(xiě)的測(cè)試的時(shí)候,總是要花上好幾分鐘才能知道這些代碼到底在干什么。

對(duì)于這個(gè)問(wèn)題,我個(gè)人會(huì)選擇使用注釋來(lái)標(biāo)注一下 given 、 when 、 then,并且給 IDEA 設(shè)置了 live template 方便插入它們。

又不是不能用的參數(shù)化測(cè)試

如果說(shuō)不能清晰地表達(dá)測(cè)試意圖這個(gè)問(wèn)題還有一些 workaround 可以繞過(guò)去的話,JUnit 那僅僅是能用的參數(shù)化測(cè)試功能就沒(méi)有什么好辦法可以繞過(guò)去了。

JUnit 提供了各種 Source 注解來(lái)為參數(shù)化測(cè)試提供數(shù)據(jù),但是這個(gè)功能實(shí)在是太弱了,很難讓人滿意。

難以讓人滿意的第一個(gè)原因是,各種 Source 注解基本上只能支持 7 種基本類型再加上 String, Enum 和 Class 類型。如果想要使用其他類型的實(shí)例作為參數(shù)的話,就必須要使用 MethodSource 或者 ArgumentsSource 注解。

這就導(dǎo)致了第二個(gè)原因:這兩個(gè)注解需要單獨(dú)寫(xiě)一個(gè)靜態(tài)方法或一個(gè) ArgumentProvider 的實(shí)現(xiàn),這就導(dǎo)致很難把測(cè)試參數(shù)寫(xiě)到測(cè)試代碼旁邊。并且 Arguments.of() 方法并不利于閱讀測(cè)試參數(shù)。

這兩點(diǎn)導(dǎo)致測(cè)試的可讀性下降。而按照“測(cè)試即文檔”的原則,我們應(yīng)該盡力去保證測(cè)試的可讀性。

第三個(gè)原因則是來(lái)自 ParameterizedTest 注解。它的 name 字段可以使用參數(shù)的索引值來(lái)把參數(shù)填入模板中,生成更加可讀的測(cè)試名稱。

但是它的功能也僅限于此了。因?yàn)檫@個(gè)模板只能使用索引值,不能使用索引后再調(diào)用里面的方法或者字段。所以如果我們的參數(shù)是一個(gè)復(fù)雜對(duì)象,那么一定要重寫(xiě) toString 方法才能得到滿意的輸出。但是這又違背了編寫(xiě)測(cè)試的原則之一——不能為了測(cè)試而添加實(shí)現(xiàn)代碼。

如果我們一定要得到一個(gè)更加表意的測(cè)試名稱,那么添加一個(gè)專用的測(cè)試參數(shù)也能做到。但是這又會(huì)導(dǎo)致 IDE 或者構(gòu)建工具的警告,因?yàn)樗鼈冋J(rèn)為這個(gè)參數(shù)沒(méi)有被使用。

總之,盡管 JUnit 可以解決絕大多數(shù)問(wèn)題,但是在這么幾個(gè)小地方卻做的不是那么完美。

那么有沒(méi)有什么工具可以作為 JUnit 的替代呢?當(dāng)然是有的。下面我將按照我接觸的順序來(lái)介紹兩種種測(cè)試框架。可以在 GitHub 上找到下面例子的完整代碼。

使用 Spock 作為測(cè)試框架

Spock是一個(gè)用 Groovy 編寫(xiě)的測(cè)試框架,按照 given/when/then 的結(jié)構(gòu)定義 dsl,能夠讓測(cè)試更加的語(yǔ)義化。它的一大特點(diǎn)是 Data Driven Test,可以方便的編寫(xiě)參數(shù)化測(cè)試。

我曾在兩個(gè)項(xiàng)目上嘗試過(guò)使用 Spock 作為測(cè)試框架,幾乎沒(méi)有遇到過(guò)無(wú)法解決的問(wèn)題。

如何使用 Spock

我們來(lái)看一個(gè)最簡(jiǎn)單的例子:

class MarsRoverSpockTest extends Specification { // 1
def "should return mars rover position and direction when mars rover report"() { //2
given: // 3.1
def marsRover = MarsRoverFixture.buildMarsRover(
position: new Position(1, 2),
direction: Direction.EAST,
)

when: // 3.2
def marsRoverInfo = marsRover.report()

then: // 3.3
marsRoverInfo.position == new Position(1, 2)
marsRoverInfo.direction == Direction.EAST
}
}

(1) 每一個(gè)測(cè)試都需要繼承抽象類 Specification;

(2) 可以使用字符串來(lái)命名測(cè)試;

(3) Spock 定義了一些 block,這里的 given 、 when 、 then 都是 block。

  • given block 負(fù)責(zé)測(cè)試的 setup 工作
  • when block 可以是任意代碼,不過(guò)最好是對(duì)測(cè)試目標(biāo)的調(diào)用。它總是和 then 一起出現(xiàn)
  • then block 用來(lái)斷言。這里不需要任何的 assertion,只需要編寫(xiě)返回值是 boolean 的表達(dá)式即可

Spock 有非常友好的測(cè)試報(bào)告輸出。如果我們把上面的斷言特意改錯(cuò),就能得到這樣的測(cè)試輸出:

Condition not satisfied:

movedMarsRover.position == movedPosition
| | | |
| | | Position(x=-2, y=2)
| | false
| Position(x=-1, y=2)
MarsRover(position=Position(x=-1, y=2), direction=WEST)

在這個(gè)輸出里面,我們可以清晰的看出表達(dá)式兩端的值是什么,非常便于 debug。

特點(diǎn)

(1) 使用字符串命名測(cè)試方法

在前面的例子中,我們可以看到測(cè)試的方法名是使用字符串來(lái)命名,不需要像 JUnit 一樣遵循 Java 方法的命名規(guī)則。這樣我們就不用糾結(jié)使用什么樣的命名方法,只需要像寫(xiě)一句話一樣來(lái)編寫(xiě)測(cè)試方法名稱。

(2) 語(yǔ)義化的結(jié)構(gòu)

在前面的例子中,我們看到了 block 的概念。它可以幫助我們更好的組織代碼結(jié)構(gòu),寫(xiě)出更加便于閱讀的代碼。其實(shí)在每一個(gè) block 聲明之后,我們還可以在添加一個(gè)字符串,達(dá)到注釋的作用。比如:

given: "a mars rover at position 1,2 and direction is north"

除了上面的例子里看到的,Spock 還提供了 cleanup 、 expect 、 where 這三個(gè) block。詳細(xì)信息可以看看它的??文檔??。

因?yàn)?Spock 強(qiáng)制要求使用 block 來(lái)組織測(cè)試代碼,這樣就可以強(qiáng)迫我們寫(xiě)出結(jié)構(gòu)化的代碼,更加便于閱讀。

(3) 使用 data table 構(gòu)造參數(shù)化測(cè)試

對(duì)于參數(shù)化測(cè)試,我們?cè)賮?lái)看一個(gè)例子。

def "should move mars rover from position 1,2 forward to #movedPosition.x,#movedPosition.y when direction is #direction and move length is #length"() { 
given:
def marsRover = MarsRoverFixture.buildMarsRover(
position: new Position(1, 2),
direction: direction,
)

when:
def movedMarsRover = marsRover.forward(length)

then:
movedMarsRover.position == movedPosition
movedMarsRover.direction == direction

where:
direction | length || movedPosition
Direction.EAST | 2 || new Position(3, 2)
Direction.WEST | 3 || new Position(-2, 2)
Direction.SOUTH | -1 || new Position(1, 3)
Direction.NORTH | 1 || new Position(1, 3)
}

我們可以看到代碼的最后一段是一個(gè) where block,這是 Spock 中用來(lái)定義數(shù)據(jù)測(cè)試的數(shù)據(jù)的地方。例子中的寫(xiě)法被稱作 data table。盡管 Spock 還支持一些其他的寫(xiě)法,但是我個(gè)人認(rèn)為 data table 是一個(gè)更加可讀的寫(xiě)法,所以這也是我最常使用的寫(xiě)法。并且這個(gè)寫(xiě)法不需要手動(dòng)調(diào)整格式,IDEA 支持自動(dòng) format,堪稱完美。

我們還可以留意一下方法名。方法名中有幾個(gè)以 # 開(kāi)頭的字符串,它們其實(shí)是在引用 data table 中定義的變量。這種通過(guò)變量名引用的方式可讀性遠(yuǎn)遠(yuǎn)大于 JUnit 的索引值的方式。并且我們可以看到 #movedPosition.x 這樣的表達(dá)式,它們可以直接使用這些對(duì)象中的字段值來(lái)生成方法名,不需要依賴于對(duì)象的 toString 方法。

對(duì)于斷言失敗的測(cè)試,同樣會(huì)像上面的例子一樣在測(cè)試輸出中打印具體的數(shù)據(jù),方便定位到測(cè)試失敗的用例。

與 JUnit 對(duì)比起來(lái),Spock 的 data table 既讓參數(shù)列表和測(cè)試方法放到了一起,又能支持任意類型的參數(shù),還可以沒(méi)有副作用地定義參數(shù)名稱,算是把 JUnit 參數(shù)化測(cè)試的痛點(diǎn)都解決了。

(4) 簡(jiǎn)潔的斷言

在上面的例子中,我們看到 Spock 的斷言十分簡(jiǎn)潔,不需要像使用 assertj 一樣寫(xiě)很長(zhǎng)的 assertThat(xxx).isEqualTo(yyy),只需要一個(gè)返回 boolean 的表達(dá)式就可以了。

甚至可以把多行斷言提取到一個(gè)方法中,返回他們與運(yùn)算的結(jié)果。

(5) 使用 Groovy 編寫(xiě)測(cè)試

使用 Groovy 來(lái)編寫(xiě)測(cè)試,可以說(shuō)既是優(yōu)點(diǎn),也是缺點(diǎn)。

優(yōu)點(diǎn)是在于 Groovy 是動(dòng)態(tài)語(yǔ)言,我們可以利用這一特性在測(cè)試中少寫(xiě)一些啰嗦的代碼。并且在前面的例子中,斷言里面獲取的 marsRover.position 字段本身是 private 字段,測(cè)試仍然可以正常執(zhí)行。這些都是由 Groovy 帶來(lái)的靈活性。

缺點(diǎn)在于這是一個(gè)相對(duì)小眾的語(yǔ)言。如果不是因?yàn)?Gradle,或許不會(huì)有多少人熟悉它的語(yǔ)法。這也會(huì)導(dǎo)致人們?cè)谶x擇它時(shí)會(huì)變得更加謹(jǐn)慎。

(6) 與 IDE 的完美集成

我一直使用的 IDEA 是能夠完美適配它的。除了前面提到的 format data table ,最主要的是 IDEA 能像執(zhí)行 JUnit 一樣執(zhí)行它,并且不需要任何的配置。

缺點(diǎn)

我在第一個(gè)項(xiàng)目里面使用 Spock 時(shí),幾乎沒(méi)有發(fā)現(xiàn)它有什么缺點(diǎn),以至于在后來(lái)的項(xiàng)目中總是在問(wèn) TL 能不能把它加到項(xiàng)目里來(lái)。

但是后來(lái)在一個(gè) Kotlin 項(xiàng)目中嘗試使用它時(shí),卻遇到一些問(wèn)題。

與 Kotlin 的集成問(wèn)題:

(1) 無(wú)法識(shí)別 Kotlin 的語(yǔ)法糖

Groovy 不能直接識(shí)別到 Kotlin 代碼中的各種語(yǔ)法糖,這就讓測(cè)試寫(xiě)起來(lái)有那么一點(diǎn)點(diǎn)不舒服。

比如命名參數(shù)。其實(shí) Groovy 也支持命名參數(shù),但是語(yǔ)法和 Kotlin 不同。這就顯得有一點(diǎn)尷尬。不過(guò)這個(gè)問(wèn)題可以通過(guò)為測(cè)試編寫(xiě)一些 fixutre 之類的代碼來(lái)幫助處理這里問(wèn)題。比如下面這個(gè) Kotlin 類型:

data class MarsRover(  
private val position: Position,
private val direction: Direction,
)

我們可以為它編寫(xiě)一個(gè) fixture,就能在測(cè)試?yán)锩嬉彩褂妹麉?shù)了:

class MarsRoverFixture {  
@NamedVariant
static MarsRover buildMarsRover(position, direction) {
new MarsRover(position, direction)
}
}

其他的一些語(yǔ)法問(wèn)題也基本都能繞過(guò),本質(zhì)思路就是把要測(cè)試的代碼想象成編譯后的 Java,這樣就能找到繞過(guò)的辦法。

(2) 沒(méi)有對(duì) final class 的 mock 支持

這是一個(gè)基本繞不過(guò)去的問(wèn)題。Kotlin 里面的類型默認(rèn)都是 final,不能再被繼承。但是 Spock 的 mock 卻需要為要 mock 的對(duì)象的類型創(chuàng)建一個(gè)子類。這就導(dǎo)致我們不能去 mock 那些類型。其實(shí)這個(gè)問(wèn)題不是 Spock 特有的,Mockito 也有這個(gè)問(wèn)題。只不過(guò)在使用 JUnit 時(shí)我們會(huì)選擇用 MockK 作為 Kotlin 項(xiàng)目的 mock 工具,而不是 Mockito。

解決這個(gè)問(wèn)題的策略有好幾個(gè):

  • 盡可能不去 mock。這要求我們?cè)O(shè)計(jì)出更容易測(cè)試的代碼,這樣就可以避免在測(cè)試中使用 mock。
  • 因?yàn)?Spring 組件需要被繼承,所以會(huì)使用 Kotlin All-open compiler 來(lái)為 Spring 提供支持,我們就可以 mock 這些 Spring Component

在寫(xiě)這篇文章的時(shí)候,發(fā)現(xiàn)一個(gè)很久沒(méi)有更新的倉(cāng)庫(kù) kotlin-test-runner,也許可以借鑒一下這里的思路來(lái)解決這個(gè)問(wèn)題。

(3) 與 JUnit 的兼容問(wèn)題

對(duì)于上一個(gè)問(wèn)題,我們當(dāng)時(shí)還有一個(gè) workaround,那就是使用 JUnit5 + MockK 來(lái)編寫(xiě)那些需要 mock 的測(cè)試。但是那個(gè)時(shí)候的 Kotlin 版本還比較低,沒(méi)有遇到和 JUnit 的兼容問(wèn)題。

兼容問(wèn)題是 JUnit 在編寫(xiě) Spring 集成測(cè)試的時(shí)候,如果有 mock bean 的需求,需要使用 springmock 里面的 @MockkBean 注解。但是從 kotlin 1.5.30 開(kāi)始,這個(gè)庫(kù)就不能和 Spock 編寫(xiě)的 Spring 集成測(cè)試兼容,會(huì)出現(xiàn) NPE 問(wèn)題。這個(gè)問(wèn)題在使用 Kotlin 對(duì) Specification 子類進(jìn)行反射時(shí)會(huì)出現(xiàn)。

這個(gè)問(wèn)題一直到 Kotlin 1.6.20-M1 才修復(fù)。

Groovy 語(yǔ)言的學(xué)習(xí)成本:

就像前面提到過(guò)的,使用 Groovy 還是有一些學(xué)習(xí)成本的。如果團(tuán)隊(duì)里沒(méi)有熟悉它的人,可能會(huì)走一點(diǎn)彎路。

使用 Kotest 作為測(cè)試框架

Kotest 是在無(wú)意中發(fā)現(xiàn)的測(cè)試框架,還沒(méi)有在實(shí)際的項(xiàng)目中實(shí)踐過(guò)。所以這里只能分享一下如何使用,沒(méi)有什么經(jīng)驗(yàn)分享。

如何使用 Kotest

我們來(lái)看一個(gè)例子:

class MarsRoverKotestTest : BehaviorSpec({  
given("a mars rover") {
val marsRover = MarsRover(
position = Position(1, 2),
direction = Direction.EAST,
)
`when`("it report information") {
val (position, direction) = marsRover.report()
then("get it's position and direction") {
position shouldBe Position(1, 2)
direction shouldBe Direction.EAST
}
}
}
})

這是一種 BDD 風(fēng)格的測(cè)試。Kotest 使用 BehaviorSpec 類封裝起來(lái)。

在 then 中,我們沒(méi)有看到常見(jiàn)的 assertThat() 語(yǔ)句,取而代之的是 Kotest 的 assertion 庫(kù)提供的方法。

特點(diǎn)

(1) 豐富的測(cè)試風(fēng)格支持

除了上面的例子,我們還有很多的測(cè)試風(fēng)格可以選擇,這在它的文檔中有介紹:??Testing Styles | Kotest??。

在這些風(fēng)格中,測(cè)試名稱都是通過(guò)字符串來(lái)編寫(xiě)的。如前面所說(shuō),這樣我們就不用像使用 JUnit 一樣糾結(jié)測(cè)試方法的命名風(fēng)格,只管描述測(cè)試目的就可以來(lái)。

然而除了 BDD 這種測(cè)試風(fēng)格外,其他的測(cè)試風(fēng)格都沒(méi)有對(duì)測(cè)試代碼的組織有任何強(qiáng)制要求。這就需要團(tuán)隊(duì)為測(cè)試代碼測(cè)組織達(dá)成一致并維護(hù)它。這和 JUnit 沒(méi)有什么區(qū)別。

(2) 對(duì) data driven test 的支持

Kotest 提供了擴(kuò)展來(lái)支持 data driven test。當(dāng)然,不使用這個(gè)擴(kuò)展也可以進(jìn)行,比如用 list 構(gòu)造好數(shù)據(jù)之后 foreach 創(chuàng)建測(cè)試。不過(guò)這里的例子我們還是使用這個(gè)擴(kuò)展來(lái)演示。

class MarsRoverKotestTest : FunSpec({  
context("data test") {
withData(
nameFn = { (direction, length, position) ->
"should move mars rover from 1,2 to ${position.x},${position.y} when direction is $direction and move length is $length"
},
Triple(Direction.EAST, 2, Position(3, 2)),
Triple(Direction.WEST, 3, Position(-2, 2)),
Triple(Direction.SOUTH, -1, Position(1, 3)),
Triple(Direction.NORTH, 1, Position(1, 3)),
) { (direction, length, movedPosition) ->
val marsRover = MarsRover(
position = Position(1, 2),
direction = direction,
)
val movedMarsRover = marsRover.forward(length)
movedMarsRover.report().position shouldBe movedPosition
movedMarsRover.report().direction shouldBe direction
}
}
})

雖然這個(gè) data driven test 相對(duì)于 Spock 的 data table 來(lái)講沒(méi)有那么直觀,但是對(duì)比 JUnit 的話,能夠方便的自定義測(cè)試方法名、支持任意類型的參數(shù)并且測(cè)試數(shù)據(jù)與測(cè)試代碼可以放在一起,已經(jīng)算是一個(gè)巨大的進(jìn)步了。

(3) 簡(jiǎn)潔的斷言

在上面的例子里面我們看到,Kotest 提供了自己的斷言庫(kù),不需要再寫(xiě)冗長(zhǎng)的 assertThat() 之類的語(yǔ)句。

(4) 使用 Kotlin 編寫(xiě),能與 Kotlin 項(xiàng)目完美結(jié)合

使用 Kotlin 來(lái)編寫(xiě)測(cè)試,可以使用到 Kotlin 里面的各種語(yǔ)法糖。這樣就不用像 Spock 一樣在語(yǔ)法切換中掙扎。

(5) 支持 MockK

同樣的,因?yàn)?Kotest 的測(cè)試使用 Kotlin 編寫(xiě),自然是支持 MockK 的。這樣就能利用 MockK 的特性,支持對(duì) final class 的 mock。

(6) 與 JUnit 兼容

因?yàn)?Kotest 是基于 JUnit 平臺(tái)的,所以是能和 JUnit 兼容的,不會(huì)出現(xiàn)上面的 Spock 那樣的問(wèn)題。

缺點(diǎn)

因?yàn)闆](méi)有在實(shí)際的項(xiàng)目中實(shí)踐過(guò),所以目前沒(méi)有發(fā)現(xiàn)很多的缺點(diǎn)。

(1) 與 IDEA 和 Gradle 的集成不夠完美

這個(gè)問(wèn)題的表現(xiàn)是在 IDEA 里面無(wú)法執(zhí)行單個(gè)測(cè)試方法。但是細(xì)究后發(fā)現(xiàn),實(shí)際上是和 gradle 的集成不夠好。

默認(rèn)情況下,IDEA 會(huì)使用 gradle 來(lái)執(zhí)行測(cè)試。執(zhí)行單個(gè)測(cè)試的命令是 gradle test --tests "xxx.Class.yyyMethod"。對(duì)于 JUnit,這里的 class 和 method 是很直觀的類名和方法名。但是 Kotest 的寫(xiě)法卻不是編寫(xiě)類里面的方法,而是調(diào)用方法生成測(cè)試。所以 gradle 的這個(gè)命令就沒(méi)有辦法生效,也就沒(méi)有辦法只執(zhí)行一個(gè)測(cè)試方法了。

在把 IDEA 的配置更新成使用 IDEA 來(lái)運(yùn)行測(cè)試后,在 mac 上能夠正常執(zhí)行單個(gè)測(cè)試方法。

不要使用 Spek

前面介紹了兩種值得一試的測(cè)試框架,這里再介紹一種不建議使用的框架。

當(dāng)初想要嘗試這個(gè)框架,是因?yàn)榭吹接芯W(wǎng)友說(shuō)這是 Kotlin 版本的 Spock 。但是實(shí)踐下來(lái)并沒(méi)有發(fā)現(xiàn)它有和 Spock 類似的功能,并且還出現(xiàn)了這些痛點(diǎn):

  • 與其他測(cè)試框架混合使用的問(wèn)題:當(dāng)與其他測(cè)試框架混合使用時(shí),Spek 測(cè)試總是會(huì)先執(zhí)行。哪怕我們?cè)?IDEA 里面只想執(zhí)行一個(gè) JUnit 的單元測(cè)試,Spek 也會(huì)先把自己的所有測(cè)試跑完,然后才會(huì)執(zhí)行到我們想要執(zhí)行的測(cè)試。這就意味著在 Speck 測(cè)試編寫(xiě)了很多之后,執(zhí)行其他測(cè)試就會(huì)等待很久。
  • 不能編寫(xiě) Spring 集成測(cè)試。
  • 我在寫(xiě) demo 的時(shí)候發(fā)現(xiàn),它的 IDEA 插件在 Windows 上面無(wú)法工作。

如果這些痛點(diǎn)你都能忍,那我也不建議使用這個(gè)框架,畢竟上面已經(jīng)有更好的選擇了。

總結(jié)

現(xiàn)在我們有了兩個(gè) JUnit 以外的測(cè)試框架選擇。當(dāng)然它們也不是完美的,JUnit 仍然是那個(gè)最穩(wěn)定、風(fēng)險(xiǎn)最低的那一個(gè)。但如果你想嘗試一下這兩個(gè)框架的話,可以考慮一下這些方面:

(1) 生產(chǎn)代碼的編程語(yǔ)言:

  • 如果是 Kotlin,那么可以考慮 Kotest,不要考慮 Spock
  • 如果是 Java,那么這兩個(gè)都值得考慮

(2) 語(yǔ)言熟悉程度:Kotlin 明顯是比 Groovy 更加流行,這個(gè)角度考慮的話 Kotest 是更優(yōu)的選擇

(3) 測(cè)試框架的流行程度(這方面我不知道有什么評(píng)價(jià)標(biāo)準(zhǔn),只是作為參考):

  • 兩個(gè)框架在 GitHub 上的 star 數(shù)量半斤八兩,一個(gè) 3.1k,一個(gè) 3.2k(JUnit 也才 4.9k)
  • 在 MVNRepository 上,Spock 的 usage 明顯高于 Kotest

(4) IDEA 的集成:

  • Spock 在這方面完全沒(méi)有問(wèn)題
  • Kotest 需要安裝插件,并且需要配置才能運(yùn)行單個(gè)測(cè)試

(5Gradle 集成:

  • Spock 完美集成
  • Kotest 不能執(zhí)行單個(gè)測(cè)試
責(zé)任編輯:趙寧寧 來(lái)源: Thoughtworks洞見(jiàn)
相關(guān)推薦

2017-01-14 23:42:49

單元測(cè)試框架軟件測(cè)試

2024-10-16 16:09:32

2009-06-01 10:47:32

jboss seam例jboss seam開(kāi)jboss seam

2023-12-24 10:00:35

Java單元測(cè)試

2009-08-19 09:00:48

單元測(cè)試框架自動(dòng)化測(cè)試

2010-08-27 09:11:27

Python單元測(cè)試

2023-07-26 08:58:45

Golang單元測(cè)試

2011-05-16 16:52:09

單元測(cè)試徹底測(cè)試

2017-01-16 12:12:29

單元測(cè)試JUnit

2017-01-14 23:26:17

單元測(cè)試JUnit測(cè)試

2022-04-27 08:17:07

OCMock單元測(cè)試集成

2011-06-14 15:56:42

單元測(cè)試

2020-08-18 08:10:02

單元測(cè)試Java

2024-04-26 11:14:34

C#單元測(cè)試框架

2017-03-23 16:02:10

Mock技術(shù)單元測(cè)試

2021-05-05 11:38:40

TestNGPowerMock單元測(cè)試

2020-05-07 17:30:49

開(kāi)發(fā)iOS技術(shù)

2011-07-04 18:16:42

單元測(cè)試

2011-06-14 15:39:46

單元測(cè)試

2012-05-21 09:41:54

XcodeiOS單元測(cè)試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美成人午夜精品免费| 国产免费内射又粗又爽密桃视频 | 精品久久久久久久久中文字幕| 国外成人在线视频网站| 99久久久久久久久| 小说区亚洲自拍另类图片专区| 日韩一级片在线播放| 免费无码不卡视频在线观看| 色三级在线观看| 国产麻豆欧美日韩一区| 91产国在线观看动作片喷水| 亚洲 欧美 国产 另类| 57pao国产一区二区| 欧美午夜精品一区二区三区 | av网站免费在线看| 综合伊人久久| 91福利国产精品| www.亚洲视频.com| 里番在线观看网站| 26uuu国产一区二区三区 | 美女扒开腿让男人桶爽久久动漫| 欧美无乱码久久久免费午夜一区| 成人性生活视频免费看| 午夜免费播放观看在线视频| 不卡一区二区三区四区| 成人免费xxxxx在线观看| 欧美不卡视频在线观看| 天堂美国久久| 在线播放亚洲激情| 久久人人爽人人人人片| 欧美电影院免费观看| 欧美中文字幕一区二区三区| 日韩国产欧美亚洲| 国内在线视频| 一区二区三区四区高清精品免费观看 | 国产精品久久久久久久午夜片| 久久久精品动漫| 亚洲国产精品suv| 精品一二三四在线| 国产男女猛烈无遮挡91| 中文字幕av第一页| 久久动漫亚洲| 欧美在线观看网站| 全部毛片永久免费看| 一区免费视频| 欧美精品videossex性护士| 一区二区三区四区五区| 天天射—综合中文网| 中文字幕欧美亚洲| 极品人妻videosss人妻| 啪啪亚洲精品| 亚洲一品av免费观看| 中文人妻一区二区三区| 人人精品视频| 亚洲美女精品成人在线视频| 国产精品伦子伦| 你懂的一区二区三区| 国产婷婷97碰碰久久人人蜜臀 | 国产偷国产偷精品高清尤物| 久久免费看av| 国产视频福利在线| 中文字幕不卡一区| 在线观看精品视频| av官网在线播放| 亚洲一二三区视频在线观看| www精品久久| 在线视频超级| 欧美性三三影院| 欧美大片久久久| 玖玖精品一区| 亚洲国产私拍精品国模在线观看| 国产原创剧情av| 亚洲综合福利| 中文字幕久久精品| 少妇人妻丰满做爰xxx| 午夜电影亚洲| 欧美一区二区三区图| 老熟妇一区二区三区| 久久精品国产在热久久| 91久久精品www人人做人人爽| 成人小说亚洲一区二区三区| 91视频com| 亚洲视频小说| av影片在线| 欧洲另类一二三四区| 欧美性受xxxx黒人xyx性爽| 亚洲欧美日本国产| 亚洲系列中文字幕| 9999热视频| 性xx色xx综合久久久xx| 国产在线视频欧美| 成人免费观看在线视频| 久久美女艺术照精彩视频福利播放 | 欧美在线播放视频| 国产又粗又长又大视频| www.亚洲在线| 一区一区视频| 欧美男男激情videos| 欧美男女性生活在线直播观看| 四虎永久免费观看| 成人激情免费视频| 国模叶桐国产精品一区| 曰批又黄又爽免费视频| 不卡视频在线看| 麻豆md0077饥渴少妇| 日韩欧美精品一区二区三区| 777久久久精品| 中文幕无线码中文字蜜桃| 亚洲电影影音先锋| 国产成人精品日本亚洲专区61| 国产激情视频在线播放| 国产欧美一区二区三区鸳鸯浴 | 免费在线日韩av| 97操在线视频| 三区四区在线视频| 欧美三级免费观看| 在线观看一区二区三区视频| 不卡视频在线| 欧美中文字幕在线视频| 亚洲成人黄色片| 国产精品久久久久久户外露出| 免费毛片小视频| 亚洲三级av| 超碰91人人草人人干| 国产精华7777777| 99re亚洲国产精品| 久久精品xxx| 国产不卡精品| 久久久国产精品亚洲一区| 啪啪小视频网站| 国产亚洲短视频| 3d动漫一区二区三区| 一本一道久久a久久| 久久综合88中文色鬼| 自拍偷拍福利视频| 久久久91精品国产一区二区精品| 日日摸日日碰夜夜爽无码| 欧美精品三级在线| 麻豆国产va免费精品高清在线| 亚洲午夜无码久久久久| 久久午夜老司机| 国内性生活视频| 同性恋视频一区| 性亚洲最疯狂xxxx高清| 免费看av毛片| 污片在线观看一区二区| 97精品人妻一区二区三区蜜桃| 欧美日韩国产探花| 成人三级在线| a毛片在线看免费观看| 欧美高清www午色夜在线视频| 美国精品一区二区| 久久国产精品色婷婷| 中文字幕人成一区| 国产精品777777在线播放| 超碰精品一区二区三区乱码| 国产xxxxxx| 一区二区三区高清在线| 亚洲午夜久久久久久久久| 最新日韩欧美| 日本高清不卡三区| 精品网站在线| 久久精品国产电影| 国产成人久久精品77777综合 | 久久久精品天堂| 久久久久久三级| 欧美超碰在线| 18成人在线| 小早川怜子影音先锋在线观看| 亚洲欧美日韩高清| 亚洲系列在线观看| 一区二区免费看| 国产精品嫩草av| 日韩av电影天堂| 欧美aaa在线观看| 99re8这里有精品热视频8在线| 97在线观看免费| 国产福利片在线| 欧美一区中文字幕| 日韩少妇高潮抽搐| 国产女同性恋一区二区| 中文字幕资源在线观看| 在线成人欧美| 天堂一区二区三区| 91麻豆精品激情在线观看最新| 97视频免费看| 3p在线观看| 亚洲成av人影院在线观看| 波多野结衣视频观看| 亚洲靠逼com| 日本激情小视频| 国产精品一区二区免费不卡| 日韩精品一区二区三区不卡 | 久久久精品天堂| 亚洲热在线视频| 久久久人人人| 黄色成人在线免费观看| 精品成人影院| 国产精品久久亚洲| 欧美系列精品| 91精品国产高清久久久久久| 快射av在线播放一区| 亚洲欧美中文日韩v在线观看| 国产模特av私拍大尺度| 色诱亚洲精品久久久久久| 欧美日韩在线视频免费| 久久久久久一二三区| 99免费观看视频| 久久精品99国产精品| 国产一区二区在线视频播放| 一区二区不卡| 亚洲mv在线看| 一区二区三区韩国免费中文网站| 666精品在线| 欧洲亚洲精品久久久久| 国产精品91在线| 国产调教在线| 欧美多人爱爱视频网站| 欧美私人网站| 曰本色欧美视频在线| 欧美偷拍视频| 亚洲第一偷拍网| 99在线小视频| 91精品婷婷国产综合久久竹菊| 日本三级欧美三级| jzzjzzjzz亚洲成熟少妇| av日韩亚洲| 精品国产乱码久久久久久久| 在线视频 91| 在线中文字幕一区| 你懂的国产视频| 一个色在线综合| 欧美一区二区三区爽爽爽| 国产精品每日更新| 色噜噜噜噜噜噜| 国产亚洲成aⅴ人片在线观看 | 日本人体一区二区| 欧美日韩成人| 欧美这里只有精品| 国产一区二区三区四区三区四 | 337p亚洲精品色噜噜| 中文在线资源天堂| 欧美性生交片4| 中文在线字幕免费观| 欧美色图片你懂的| 最近中文字幕在线观看视频| 在线观看视频一区| 夜夜爽妓女8888视频免费观看| 色综合久久中文综合久久97| 天堂а√在线中文在线新版| 欧美午夜宅男影院在线观看| 国产精品自拍99| 一本久久综合亚洲鲁鲁五月天 | 色偷偷男人天堂| 国产精品福利av| 疯狂撞击丝袜人妻| 亚洲美女区一区| 欧美成欧美va| 亚洲丰满少妇videoshd| 特一级黄色大片| 一本大道久久a久久精二百| 丁香社区五月天| 欧美精品在线一区二区| 国产探花精品一区二区| 精品久久一区二区| 亚洲 国产 欧美 日韩| 亚洲欧美日韩国产精品| 午夜在线小视频| 色综合久久久久久中文网| 99色在线观看| 国产成人精品一区二区三区| 成人在线视频观看| 999精品视频一区二区三区| 国产成人澳门| 日韩精品伦理第一区| 经典一区二区| 干日本少妇视频| 亚洲精品四区| 少妇网站在线观看| 高清不卡在线观看| 国产精品jizz| 亚洲视频在线观看三级| 强乱中文字幕av一区乱码| 精品毛片网大全| 一本一道精品欧美中文字幕| 精品国产乱码久久久久久老虎| 国内在线精品| 色综合色综合网色综合| 激情都市亚洲| 亚洲xxxx18| 亚洲人挤奶视频| 大桥未久一区二区三区| 亚洲一区二区三区高清| 一区二区三区四区毛片| www成人在线观看| 91高清免费观看| 日韩欧美国产成人| 国产www免费观看| 夜夜嗨av色一区二区不卡| 直接在线观看的三级网址| 日韩免费在线观看视频| 视频亚洲一区二区| 色狠狠久久av五月综合| 在线精品亚洲| 肉色超薄丝袜脚交| 国产欧美精品一区| 91精品国产乱码在线观看| 日韩一区二区三区电影 | 国产精品视频yy9299一区| www.天天色| 91精品欧美综合在线观看最新| 人操人视频在线观看| 欧美激情久久久久| 亚洲图片小说区| 日本欧美精品久久久| 999亚洲国产精| 国产精品久久久久野外| 国产精品萝li| 中文字幕一区二区人妻视频| 日韩精品视频免费在线观看| 18av在线视频| 国产中文字幕日韩| 欧美色婷婷久久99精品红桃| 女人和拘做爰正片视频| 成人黄色在线网站| 欧美日韩偷拍视频| 日韩一级完整毛片| 在线视频观看国产| 亚洲影影院av| 一区二区国产在线| 热久久久久久久久| 国产精品久久看| 中文亚洲av片在线观看| 国产一区二区三区18| 三级成人黄色影院| 欧美激情导航| 乱码第一页成人| 强伦人妻一区二区三区| 欧美色播在线播放| 天堂在线视频免费| 91精品国产乱码久久久久久蜜臀 | 久久99精品国产| 一本色道久久88| 欧美日韩一本到| 欧美午夜电影一区二区三区| 成人高h视频在线| 91精品亚洲| 久久综合桃花网| 一区二区三区中文字幕精品精品| 国产精品久久久久久免费免熟| 久久久精品国产网站| 国产精品3区| 日韩国产成人无码av毛片| 国产乱子轮精品视频| 激情综合五月网| 精品国产99国产精品| 九色porny丨首页入口在线| 精品国产一区二区三区免费| 亚洲欧美清纯在线制服| 免费看裸体网站| 欧美日韩视频一区二区| 最新超碰在线| 精品无码久久久久国产| 美日韩精品视频| 日韩av网站在线播放| 日韩一二在线观看| 黄毛片在线观看| 日韩精品欧美专区| 狠狠狠色丁香婷婷综合激情| 欧美日韩三级在线观看| 亚洲国产一区二区三区四区| 婷婷综合六月| 欧美 日韩 国产 在线观看| 国产成人午夜电影网| www..com国产| 最新国产成人av网站网址麻豆| 国产一区 二区| 国产在线青青草| 国产精品福利在线播放| www.我爱av| 国产成人短视频| 亚洲女同中文字幕| 无码人妻精品一区二区三区温州| 欧美午夜理伦三级在线观看| 久草在线资源站资源站| 欧洲亚洲一区二区三区四区五区| 久久电影网电视剧免费观看| 国产五月天婷婷| 在线成人中文字幕| 国产精品qvod| 日韩欧美国产片| 婷婷六月综合网| 免费黄色在线看| 久久riav二区三区| 国产在线一区二区| 国产精品一区二区三区四| 久久精品视频亚洲| 中文字幕中文字幕精品| ass极品水嫩小美女ass| 欧美中文字幕久久|