ScalaMock- 提升用例完整度利器

相信每個軟件從業(yè)者都意識到測試在項目中重要性?!皭酆藿豢棥弊阋孕稳菸覀兒蜏y試用例關(guān)系。愛它為我們發(fā)現(xiàn)的巨坑,恨它則因為絞盡腦汁構(gòu)建測試場景。

毋庸置疑的是:測試用例是項目管理不可或缺部分。為了更好的服務(wù)項目,豐富而完整的測試用例是必要的。

在編寫測試用例時,函數(shù)中調(diào)用的其它函數(shù),或者引用外部資源。在不使用資源的情況下,我們想做到更好的測試,需要模擬外部函數(shù)的結(jié)果。今天的主角ScalaMock是便于我們模擬外部結(jié)果測試組件。

ScalaMock介紹

ScalaMock和JUnit, NUnit*測試框架不同,它沒有提供底層的測試代碼,而是基于兩種測試框架之上(ScalaTest和Specs2)。它類似于給測試框架提供構(gòu)建“假”的代碼運行環(huán)境或者叫“樁”。為編寫測試用例提供便捷語法糖,很甜很甜那種,哈哈。本文通過使用scalamock構(gòu)造簡單的測試場景,來展現(xiàn)其功能。

用例運行環(huán)境

一切沒有預(yù)置前提的說明文檔都必然是耍流氓,_

// Add the ScalaMock library (versions 4.0.0 onwards)
libraryDependencies += "org.scalamock" %% "scalamock" % "4.4.0" % Test
// also add ScalaTest as a framework to run the tests
//scala test inter scala mock sugar
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % Test</pre>

本文用例基于 scalmock 4.4.0scalatest 3.0.8的版本。

ScalaMock的是與非

ScalaMock 運行基于 scalatest,我們編寫的用例形式上要遵循scalatest的形式。從以下3個方面介紹scalamock.

  • 匿名函數(shù)mock
  • trait mock
  • class mock
    在介紹使用場景前,先看看scalamock語法樣式。
  • ScalaMock語法樣式

//匿名函數(shù)格式: mockFunction,入?yún)?string,int , 返回值String
val funcMock = mockFunction[String, Int, String]
// 格式 變量 expects()關(guān)鍵字+ returning關(guān)鍵字 + 返回值("wayne mock") + 附加屬性(once()只執(zhí)行一次)
funcMock expects("hello world", 18) returning "wayne mock" once() //once
//樣式含義是:當(dāng)輸入期望特定的輸入時,返回returning 值。

基于函數(shù)的Mock

函數(shù)mock關(guān)鍵字是:mockFunction.

  • 匿名函數(shù)mock

//定義函數(shù)mock
val funcMock = mockFunction[String, Int, String]
funcMock expects("hello world", 18) returning "function mock" atLeastOnce() //call at least once
?
//調(diào)用
println(funcMock("hello world", 18)) //輸出: function mock </pre>

atLeastOnce() 使用scalamock 時,設(shè)置屬性表示mock的內(nèi)容被調(diào)用的次數(shù)。

匿名函數(shù)mock使用頻率不高,更多使用trait class方式進(jìn)行mock。下文有匿名函數(shù)進(jìn)行mock的使用場景。

基于trait 的mock

trait進(jìn)行mock操作關(guān)鍵字是:mock.

源碼如下:

trait MyMockTest {
def funcOnePara(name: String): String = { //一個參數(shù)
name
}
?
def funcTwoPara(name: String, age:Int): String = name //2個參數(shù)
def funcNoArgs: String = "func no args" //無參數(shù)函數(shù)
val valPara="variable param"
}

  • mock trait 示例

    • mock trait無參數(shù)示例

val my = mock[MyMockTest]

(my.funcNoArgs _ ).expects().returning("no args") //mock funcNoArgs返回no args
?
my.funcNoArgs shouldBe("no args")//scala test 結(jié)果比較

定義了my變量之后,就可以對trait內(nèi)所有的函數(shù)進(jìn)行mock操作了。其它沒有mock的函數(shù)不影響。

trait TraitExtend extends MyMockTest
val my = mock[TraitExtend]
println(my.funNoArgs) //輸出func no args

  • trait有參調(diào)用mock

trait 有參數(shù)mock編寫方式有兩種,僅僅是寫法上差異,本質(zhì)上沒有差別

  1. 根據(jù)參數(shù)類型進(jìn)行mock

//使用指定 參數(shù)類型的方式,mock 有參函數(shù)
(my.funcOnePara(_:String)).expects("wayne") returning("one para")
println(my.funcOnePara("wayne"))

  1. 根據(jù)匿名函數(shù),使用類型推導(dǎo)方式 mock

//使用指定 匿名函數(shù)的方式,利用依賴函數(shù)推導(dǎo),mock 有參函數(shù)
(my.funcOnePara _ :String => String).expects("wayne") returning("one para")
println(my.funcOnePara("wayne"))

  • val/var變量不支持mock

我們無法在外部使用其它val變量時,模擬偽裝其結(jié)果。 只能通過其它方式,見后文。

基于class 的mock

編寫測試用例時,我們遇見更多場景是函數(shù)中調(diào)用其它類函數(shù),或者引用外部資源。在不使用資源的情況下,做到更好的測試,我們需要對外部函數(shù)進(jìn)行mock操作?;赾lass進(jìn)行mock是更普適使用場景。

class 進(jìn)行mock操作關(guān)鍵字是:mock.

//源碼示例

class MyMockClass {
var age: Int = 0
def funcMock = ""
?
}

class的構(gòu)成和trait構(gòu)成類似,因此mock的方式也是一致的。類中函數(shù)模擬示例,和trait沒有任何區(qū)別。

//mock class without param
val t = mock[MyMockClass]
(t.funcMock _ ) expects() returning("mock class function")
?
println(t.funcMock)

值得關(guān)注的是:不論mock那種類型,都是對該類型進(jìn)行操作。只影響指定輸入和返回值的函數(shù),對其它沒有影響

基于case class 的mock

樣例類在scala中是特殊的存在,即作為數(shù)據(jù)的承載者,有具備處理能力。我們采用投機的版本進(jìn)行mock,你們函數(shù)的mock。定義樣例類Person進(jìn)行mock測試。示例如下:

case class Person(age:Int, address:String)
//case class mock 一個變量或者函數(shù) 沒有mock必要
val caseMock = mockFunction[Unit,Person]
(caseMock).expects().returning(Person(age= 12))
?
println(caseMock().age)

我們看到給Personmock的方法是采用我們上文提到的匿名函數(shù)。

ScalaMock其它支持特性
  • 支持重載方法的重載

  • 支持java的類和接口

  • 支持邊界測試,即給出邊界值,拋出異常

  • 支持統(tǒng)計函數(shù)調(diào)用次數(shù),和函數(shù)調(diào)用次數(shù)類似

  • 支持對返回值類型排序

  • 支持在多線程環(huán)境進(jìn)行mock

ScalaMock 力所不及

如果看到scalaMock的實現(xiàn)原理,我們會發(fā)現(xiàn)我們常用的 單例對象、有參數(shù)class都不能進(jìn)行mock操作。

下面列舉下scalamock不支持的特征

  • 不支持 final/private等關(guān)鍵字mock

  • 不支持單例object對象 mock

    注:一個可支持mock的方法是:把單例對象修改成trait對象,或者把調(diào)用單例對象封裝到實現(xiàn)調(diào)用類中,通過mock方法形式達(dá)到mock 單例對象的目的,曲線“救國”吧。

以上敘述scalamock用法,由于其內(nèi)在的約束,要想用好scalamock,既要了解用法,更重要的是編碼的合理性。工具有優(yōu)越性建立在優(yōu)秀的代碼之上。

下一篇,介紹scala的其它mock組件。

據(jù)說點贊、評論 減少100%bug。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容