[英]What is the preferred way to implement testing mocks in Go?
我在Go中構建了一個簡單的CLI工具,用作各種密碼存儲(Chef Vault,Ansible Vault,Hashicorp Vault等)的包裝。 這部分是作為熟悉Go的練習。
在解決這個問題時,我遇到了編寫測試的情況,發現我需要為很多事情創建interfaces
,只是為了能夠模擬依賴關系。 這樣,為了進行測試,一個相當簡單的實現似乎有很多抽象。
但是,我最近在閱讀《 Go編程語言》,並找到了一個示例,其中他們以以下方式模擬了依賴關系。
func Parse() map[string]string {
s := openStore()
// Do something with s to parse into a map…
return s.contents
}
var storeFunc = func openStore() *Store {
// concrete implementation for opening store
}
// and in the testing file…
func TestParse(t *testing.T) {
openStore := func() {
// set contents of mock…
}
parse()
// etc...
}
因此,為了進行測試,我們將這個具體的實現存儲在一個變量中,然后我們可以在測試中實質上重新聲明該變量,並使它返回所需的內容。
否則,我將為此創建一個interface
(盡管當前只有一個實現)並將其注入Parse
方法。 這樣,我們可以模擬它進行測試。
所以我的問題是:每種方法的優點和缺點是什么? 什么時候更適合為模擬目的創建接口,而不是將具體函數存儲在變量中以便在測試中重新聲明?
沒有答案的“正確方法”。
說了這么多,我發現interface
方法比定義一個函數變量並為測試設置它更加通用和清晰。
以下是有關原因的一些評論:
如果您需要模擬多個函數(在您的示例中,它只是一個函數),那么function variable
方法將無法很好地擴展。
與使最終隱藏在實現中的功能變量相反,該interface
使該功能/模塊的行為更加清楚。
該interface
允許您注入帶有狀態(結構)的類型,這對於配置模擬的行為可能有用。
對於簡單的情況,您當然可以依靠“函數變量”方法,而對於更復雜的功能,可以使用“接口”,但是如果您希望保持一致並只使用一種方法,則可以使用“接口”。
為了進行測試,我傾向於使用您描述的模擬方法,而不是創建新的接口。 原因之一是AFAIK, 沒有直接的方法來識別哪些結構實現了接口 ,如果我想知道模擬是否在做正確的事情,這對我來說很重要。
這種方法的主要缺點是,該變量本質上是程序包級別的全局變量(即使未導出)。 因此,聲明全局變量的所有缺點都適用。
在您的測試中,您肯定會希望在測試完成后使用defer
將storeFunc
重新分配回其原始的具體實現。
var storeFunc = func *Store {
// concrete implementation for opening store
}
// and in the testing file…
func TestParse(t *testing.T) {
storeFuncOriginal := storeFunc
defer func() {
storeFunc = storeFuncOriginal
}()
storeFunc := func() {
// set contents of mock…
}
parse()
// etc...
}
順便說一下, var storeFunc = func openStore() *Store
不會編譯。
我以不同的方式解決這個問題。 特定
function Parse(s Store) map[string] string{
// Do stuff on the interface Store
}
您有幾個優點:
但是,這很明顯:解析是可以附加到商店的函數,它比解析周圍的商店更有意義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.