您好,登錄后才能下訂單哦!
GoStub是一款輕量級的單元測試框架,接口友好,可以對全局變量、函數或過程進行打樁。
GoStub安裝:go get github.com/prashantv/gostub
gostub用于在測試時打樁變量,一旦測試運行時,重置原來的值。
type Stubs struct {
// stubs is a map from the variable pointer (being stubbed) to the original value.
stubs map[reflect.Value]reflect.Value
origEnv map[string]envVal
}
Stubs代表一系列可以重置的打樁變量。
func Stub(varToStub interface{}, stubVal interface{}) *Stubs {
return New().Stub(varToStub, stubVal)
}
Stub使用stubVal替代存儲在varToStub變量的值,返回*Stubs
類型變量
varToStub必須是指向變量的指針。
stubVal是可賦值到變量的類型
func StubFunc(funcVarToStub interface{}, stubVal ...interface{}) *Stubs {
return New().StubFunc(funcVarToStub, stubVal...)
}
StubFunc用返回stubval值的函數替換函數變量,返回*Stubs
類型變量
funcVarToStub是指向函數變量的指針。如果函數返回多個值,返回的多個值被傳遞給StubFunc。func New() *Stubs
New返回用于打樁變量的*Stubs
變量func (s *Stubs) Reset()
Reset重置打樁的所有變量到其原始值func (s *Stubs) ResetSingle(varToStub interface{})
ResetSingle重置打樁的單個變量到其原始值func (s *Stubs) SetEnv(k, v string) *Stubs
SetEnv設置指定的環境變量到指定值func (s *Stubs) UnsetEnv(k string) *Stubs
UnsetEnv還原指定環境變量的值func (s *Stubs) Stub(varToStub interface{}, stubVal interface{}) *Stubs
Stub使用stubVal替代存儲在varToStub變量的值
varToStub必須是指向變量的指針。
stubVal是可賦值到變量的類型func (s *Stubs) StubFunc(funcVarToStub interface{}, stubVal ...interface{}) *Stubs
StubFunc用返回stubval值的函數替換函數變量,返回*Stubs
類型變量
funcVarToStub是指向函數變量的指針。如果函數返回多個值,返回的多個值被傳遞給StubFunc。
GoStub框架的使用場景如下:
A、為一個全局變量打樁
B、為一個函數打樁
C、為一個過程打樁
D、由任意相同或不同的基本場景組合而成
假設counter為被測函數中使用的一個全局整型變量,當前測試用例中假定counter的值為200,則打樁的代碼如下:
package main
import (
"fmt"
"github.com/prashantv/gostub"
)
var counter = 100
func stubGlobalVariable() {
stubs := gostub.Stub(&counter, 200)
defer stubs.Reset()
fmt.Println("Counter:", counter)
}
func main() {
stubGlobalVariable()
}
// output:
// Counter: 200
stubs是GoStub框架的函數接口Stub返回的對象,Reset方法將全局變量的值恢復為原值。
package main
import (
"io/ioutil"
"fmt"
"github.com/prashantv/gostub"
)
var configFile = "config.json"
func GetConfig() ([]byte, error) {
return ioutil.ReadFile(configFile)
}
func stubGlobalVariable() {
stubs := gostub.Stub(&configFile, "/tmp/test.config")
defer stubs.Reset()
/// 返回tmp/test.config文件的內容
data, err := GetConfig()
if err != nil {
fmt.Println(err)
}
fmt.Println(data)
}
func main() {
stubGlobalVariable()
}
通常函數分為工程自定義函數與庫函數。
假設工程中自定義函數如下:
func Exec(cmd string, args ...string) (string, error) {
...
}
Exec函數是不能通過GoStub框架打樁的。如果想要通過GoStub框架對Exec函數進行打樁,則僅需對自定義函數進行簡單的重構,即將Exec函數定義為匿名函數,同時將其賦值給Exec變量,重構后的代碼如下:
var Exec = func(cmd string, args ...string) (string, error) {
...
}
當Exec函數重構成Exec變量后,并不影響既有代碼中對Exec函數的調用。由于Exec變量是函數變量,因此一般函數變量也叫做函數。對Exec函數變量進行打樁的代碼如下:
stubs := Stub(&Exec, func(cmd string, args ...string) (string, error) {
return "test", nil
})
defer stubs.Reset()
GoStub框架專門提供了StubFunc函數用于函數打樁,對于函數的打樁代碼如下:
stubs := StubFunc(&Exec,"test", nil)
defer stubs.Reset()
工程代碼中會調用Golang庫函數或第三方庫函數,由于不能重構庫函數,因此需要在工程代碼中增加一層適配層,在適配層中定義庫函數的變量,然后在工程代碼中使用函數變量。
package Adapter
import (
"time"
"fmt"
"os"
"github.com/prashantv/gostub"
)
var timeNow = time.Now
var osHostname = os.Hostname
func getDate() int {
return timeNow().Day()
}
func getHostName() (string, error) {
return osHostname()
}
func StubTimeNowFunction() {
stubs := gostub.Stub(&timeNow, func() time.Time {
return time.Date(2015, 6, 1, 0, 0, 0, 0, time.UTC)
})
fmt.Println(getDate())
defer stubs.Reset()
}
func StubHostNameFunction() {
stubs := gostub.StubFunc(&osHostname, "LocalHost", nil)
defer stubs.Reset()
fmt.Println(getHostName())
}
使用示例:
package main
import "GoExample/GoStub/StubFunction"
func main() {
Adapter.StubTimeNowFunction()
Adapter.StubHostNameFunction()
}
沒有返回值的函數稱為過程。通常將資源清理類函數定義為過程。
package main
import (
"fmt"
"github.com/prashantv/gostub"
)
var CleanUp = cleanUp
func cleanUp(val string) {
fmt.Println(val)
}
func main() {
stubs := gostub.StubFunc(&CleanUp)
CleanUp("Hello go")
defer stubs.Reset()
}
不論是調用Stub函數還是StubFunc函數,都會生成一個Stubs對象,Stubs對象仍然有Stub方法和StubFunc方法,所以在一個測試用例中可以同時對多個全局變量、函數或過程打樁。全局變量、函數或過程會將初始值存在一個map中,并在延遲語句中通過Reset方法統一做回滾處理。
多次打樁代碼如下:
stubs := gostub.Stub(&v1, 1)
defer stubs.Reset()
// Do some testing
stubs.Stub(&v1, 5)
// More testing
stubs.Stub(&b2, 6)
多次打樁的級聯表達式代碼如下:defer gostub.Stub(&v1, 1).Stub(&v2, 2).Reset()
使用GoConvey測試框架和GoStub測試框架編寫的測試用例如下:
package main
import (
"fmt"
"testing"
"GoExample/GoStub/StubFunction"
"time"
"github.com/prashantv/gostub"
. "github.com/smartystreets/goconvey/convey"
)
var counter = 100
var CleanUp = cleanUp
func cleanUp(val string) {
fmt.Println(val)
}
func TestFuncDemo(t *testing.T) {
Convey("TestFuncDemo", t, func() {
Convey("for succ", func() {
stubs := gostub.Stub(&counter, 200)
defer stubs.Reset()
stubs.Stub(&Adapter.TimeNow, func() time.Time {
return time.Date(2015, 6, 1, 0, 0, 0, 0, time.UTC)
})
stubs.StubFunc(&CleanUp)
fmt.Println(counter)
fmt.Println(Adapter.TimeNow().Day())
CleanUp("Hello go")
})
})
}
GoStub框架可以解決很多場景的函數打樁問題,但下列復雜場景除外:
A、被測函數中多次調用了數據庫讀操作函數接口,并且數據庫為key-value型。
B、被測函數中有一個循環,用于一個批量操作,當某一次操作失敗,則返回失敗,并進行錯誤處理。
C、被測函數中多次調用了同一底層操作函數。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。