<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在 Go 中實現(xiàn) Monkey Patch

          共 2789字,需瀏覽 6分鐘

           ·

          2020-09-26 04:44

          作者:lryong
          來源:SegmentFault 思否社區(qū)?




          背景


          在進(jìn)行單元測試的時候,通過?testify框架?對測試函數(shù)的數(shù)據(jù)和所依賴的方法做 mock,但是單測出現(xiàn) panic。根據(jù)錯誤提示,被測試函數(shù)調(diào)用了 time.Now(), 因為會對比這個函數(shù)返回值, 所以本次單測沒有跑通過。下面介紹通過?monkey patch?來解決這個問題。




          問題復(fù)現(xiàn)


          示例代碼如下,HandleEvent()?處理一個 Webhook 的回調(diào)事件,使用 time.Now()?標(biāo)識事件處理的時間點:


          func?(e?*eventSrv)?HandleEvent(ctx?context.Context,?args?*EventArgs)?(*Event,?error)?{
          ????event?:=?&Event{
          ????????CreatedAt:?time.Now(),
          ????????Messages:?????????args,
          ????}
          ??????err?:=?e.eventRepo.CreateEvents(&event)
          ????if?err?!=?nil?{
          ????????fmt.Println(`error?occured?while?handing?event:`,?err)
          ????????return?nil,?err
          ????}
          ????return?event,?nil
          }


          單元測試代碼:


          func?TestService_HandleEvent_OK(t?*testing.T)?{
          ????var?(
          ????????ctx?????????=?context.Background()
          ????????createdTime?=?time.Now()
          ????????args????????=?EventArgs{
          ????????????//?Mock?Data
          ????????????...
          ????????}
          ????????createdTime?=?time.Now()
          ????????event???????=?Event{
          ????????????Messages:?args{
          ????????????????CreatedAt:?createdTime.String(),
          ????????????},
          ????????}
          ????)

          ????eventMockRepo?:=?&MockEventRepository{}
          ????eventMockRepo.On("HandleEvent",?ctx,?&args).
          ????????Return(&event,?nil)

          ????eventSrv?:=?NewEventSrv(eventMockRepo)
          ????resp,?err?:=?eventSrv.HandleEvent(ctx,?&args)

          ????assert.Nil(t,?err)
          ????assert.Equal(t,?resp,?&event)
          }


          測試文件包含了設(shè)置測試功能、進(jìn)行初始化設(shè)置和模擬數(shù)據(jù)。EventSrv 接收 EventArgs 入?yún)ⅲ祷靥幚砗蟮?response,在沒有 mock 時間(CreatedAt)的情況下,執(zhí)行單測函數(shù)會報如下錯誤:


          問題的原因是代碼在測試環(huán)境和主代碼中運行時,會有時延問題。這里的預(yù)期時間比實際時間大,因為我們在設(shè)置測試之前 mock 了時間(CreatedAt),而實際時間是在主代碼中創(chuàng)建的。


          可以通過 Monkey Patch 的方式, 來解決類似在單元測試 Mock 數(shù)據(jù)狀態(tài)不一致問題。




          Monkey Patch


          Monkey Patch 是程序在本地擴(kuò)展、或修改程序?qū)傩缘囊环N方式。是指在運行時對類或模塊的動態(tài)修改,其目的是給現(xiàn)有的第三方代碼打上補丁,以解決沒有達(dá)到預(yù)期效果的問題或功能。一般用于動態(tài)語言,比如 Python 和 Ruby。有以下應(yīng)用場景:


          1. 在運行時替換掉 classes/methods/attributes/functions
          2. 修改/擴(kuò)展第三方 Lib 的行為,而不依賴源代碼
          3. 在運行時將 Patch 的結(jié)果應(yīng)用到內(nèi)存中的狀態(tài)
          4. 修復(fù)原來代碼存在的安全問題或行為修正

          簡單來說就是 Monkey Patch 可以修改當(dāng)前運行的實例的變量狀態(tài)和行為。以上面說到的問題,就是修改 time.Now()來返回我們約定好的時間值。


          雖然 Go 是靜態(tài)編譯語言,Mockey Patch 的作用域在 Runtime,但是通過 Go 的 unsafe 包,能夠?qū)?nèi)存中函數(shù)的地址替換為運行時函數(shù)的地址。具體的原理和實現(xiàn)方式參考 =>?Monkey Ptching in Go




          解決方案


          Monkey?庫是 Monkey Patch 的一個 Go 版本實現(xiàn)。通過這個依賴包,修改 time.Now()?返回的時間:


          func?TestService_HandleEvent_OK(t?*testing.T)?{
          ????createdTime?=?time.Now()
          ??
          ??????...
          ??
          ??????//?resolve?current?time?inconsistencies
          ????monkey.Patch(time.Now,?func()?time.Time?{
          ????????return?createdTime
          ????})
          ??
          ??????...
          ??
          }


          Patch 后,當(dāng)主代碼執(zhí)行到 time.Now()時,將指向到這個給定的函數(shù),返回自定義的 Mock 值。


          注意:?因為 unsafe操作是不安全的,繞過了 Go 的內(nèi)存安全原則,所以應(yīng)該在測試環(huán)境中使用 Monkey Patch,并且只在需要的時候使用,確保真正需要 Mocking 的 testing 函數(shù)只使用這種方式。




          小結(jié)


          本文由一次單元測試沒有 mock 掉 time.Now()?的 case 引出 Monkey Patch ,介紹了它的特性和原理,并且通過 Monkey 的 Go 實現(xiàn), 解決我們在單測可能存在的一些 mock 數(shù)據(jù)不一致問題。




          參考

          1. Monkey Ptching in Go:https://bou.ke/blog/monkey-patching-in-go/
          2. Monkey patch:https://en.wikipedia.org/wiki/Monkey_patch



          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。


          -?END -

          瀏覽 61
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91你懂的| 国产AV豆花看片 | 日本a影院 | 婷婷伊人丁香 | 欧美爱爱视频福利 |