簡體   English   中英

是否可以在 golang 中模擬從 package 導入的 function?

[英]Is it possible to mock a function imported from a package in golang?

我有以下測試方法,它使用從 package 導入的 function。

import x.y.z

func abc() {
    ...
    v := z.SomeFunc()
    ... 
}

是否可以在 golang 中模擬SomeFunc()

是的,通過簡單的重構。 創建一個函數類型的zSomeFunc變量,用z.SomeFunc初始化,並讓你的包調用它而不是z.SomeFunc()

var zSomeFunc = z.SomeFunc

func abc() {
    // ...
    v := zSomeFunc()
    // ...
}

在測試中,您可以將另一個函數分配給zSomeFunc ,一個在測試中定義的函數,並執行測試想要它做的任何事情。

例如:

func TestAbc(t *testing.T) {
    // Save current function and restore at the end:
    old := zSomeFunc
    defer func() { zSomeFunc = old }()

    zSomeFunc = func() int {
        // This will be called, do whatever you want to,
        // return whatever you want to
        return 1
    }

    // Call the tested function
    abc()

    // Check expected behavior
}

請參閱相關/可能重復: 使用覆蓋信息在 Go 中測試 os.Exit 場景 (coveralls.io/Goveralls)

你可以做的一件事是:

import "x/y/z"

var someFunc = z.SomeFunc

func abc() {
    ...
    v := someFunc()
    ... 
}

在您的測試文件中,您將執行此操作。

func Test_abc() {
    someFunc = mockFunc
    abc()
}

但是請確保以並發方式執行此操作,如果您有多個TestXxx函數調用abc或設置someFunc ,則最好使用帶有someFunc字段的struct

讓函數指針和猴子修補它是其中之一。 但是,當您模擬多個函數時,您將擁有多個函數指針,並且不必要地僅使用函數指針來調用函數。

擁有一個接口並使您的功能成為實現該接口的結構的一部分的更好和推薦的想法。 完成后,您可以使用一些可用於 go 的不錯的工具生成模擬。

我一直在使用這個:

mockgen -source myModule.go -package myPackage -destination myModuleMock.go

您可以通過以下方式安裝它:

go get github.com/golang/mock

雖然創建 package 級別變量是一個可行的選項,但它有一些限制。 舉幾個例子:

  1. 它不鼓勵使用t.Parallel()運行並行測試,因為模擬 function 的不同行為可能存在競爭條件。
  2. 這是危險的,因為同一 package 中的未來開發人員可能會不小心更新此全局變量的實現。

另一種方法是將要模擬的方法作為 arguments 傳遞給 function 以啟用可測試性。 就我而言,我已經有許多客戶調用此方法,因此,我想避免違反現有合同。 所以,我最終創建了一個包裝好的 function。

例如:

import (
 z "x.y.z"
)

//this should replicate the type of function z from x.y.z
type operation func() resp

func wrappedAbc(op operation) {
  ....
  resp := op()
  ....
}

func Abc() {
  wrappedAbc(z)
}

現在為了測試實際邏輯,您將測試對wrappedAbc而不是abc的調用,並且您將向它傳遞一個模擬operation 這將允許您測試所有業務邏輯,同時不違反 API 與方法Abc的當前客戶的合同。

mockcompose使用一種允許您生成mockcompose類的方法,您可以指示mockcompose包含您選擇的依賴項閉包(從其他包中導入的任何函數)。 同時,它會生成具有本地覆蓋的主題函數的克隆副本,以便您可以對其進行測試。 您可以使用go generate嵌入代碼生成過程,因此確保您的克隆副本始終與您的代碼更改同步。

假設您有一個函數functionThatUsesGlobalFunction在包fmt中導入Sprintf

func functionThatUsesGlobalFunction(
    format string,
    args ...interface{},
) string {
    //
    // skip fansy logic...
    //

    // call out to a global function in fmt package
    return fmt.Sprintf(format, args...)
}

您的目標是在被Sprintf的包fmt使用Sprintf測試functionThatUsesGlobalFunction

為此,您可以使用mockcompose配置go generate ,如下所示:

Mocks.go

//go:generate mockcompose -n mockFmt -p fmt -mock Sprintf
//go:generate mockcompose -n mockJson -p encoding/json -mock Marshal
//go:generate mockcompose -n clonedFuncs -real "functionThatUsesGlobalFunction,fmt=fmtMock"
package clonefn

go generate mockcompose將為您生成管道類,使您能夠編寫如下測試代碼:

package clonefn

import (
    "testing"

    "github.com/stretchr/testify/mock"
    "github.com/stretchr/testify/require"
)

var fmtMock *mockFmt = &mockFmt{}

func TestClonedFuncs(t *testing.T) {
    assert := require.New(t)

    // setup function mocks
    fmtMock.On("Sprintf", mock.Anything, mock.Anything).Return("mocked Sprintf")

    // inside functionThatUsesMultileGlobalFunctions: fmt.Sprintf is mocked
    assert.True(functionThatUsesGlobalFunction_clone("format", "value") == "mocked Sprintf")
}

請查看了解詳情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM