2 回答

TA貢獻1786條經(jīng)驗 獲得超13個贊
如果目標是驗證查詢是否有效并實際針對涌入執(zhí)行,則沒有捷徑可以針對涌入實際執(zhí)行這些查詢。這些通常被認為是“集成”測試。我使用 docker-compose 發(fā)現(xiàn)這些測試與單元測試一樣可靠,并且速度足夠快,可以集成到 CI 中。在 CI 中執(zhí)行測試使本地工程師能夠輕松地運行這些測試來驗證他們的查詢更改。
我想到處都有存儲庫,即使是小型服務(wù),只是為了讓它們可測試,似乎被過度設(shè)計了。
我發(fā)現(xiàn)這是非常兩極分化的討論。測試實現(xiàn)是一種具體的實現(xiàn),它為可靠、可重復(fù)的測試鋪平了道路,這些測試支持輕松隔離和執(zhí)行代碼的特定組件。
我想對此進行單元測試,但我不知道該怎么做,
我認為這非常微妙,IMO 單元測試查詢提供了負值。價值來自于使用存儲庫接口來允許您的單元測試顯式配置您將從 influx 收到的響應(yīng),以便充分運行您的應(yīng)用程序代碼。這沒有提供關(guān)于流入的反饋,這就是為什么集成測試對于驗證您的應(yīng)用程序可以有效地配置、連接和查詢流入是必不可少的。當您部署應(yīng)用程序時,此驗證會隱式發(fā)生,此時它在反饋方面變得比在本地和在 CI 中使用集成測試驗證它要昂貴得多。
我創(chuàng)建了一個圖表來嘗試說明這些差異:
帶有存儲庫的單元測試專注于您的應(yīng)用程序代碼,并且在與流入有關(guān)的任何事情上提供很少的反饋/價值。集成測試對于驗證您的客戶端很有用(可能會根據(jù)測試執(zhí)行的位置擴展到您的應(yīng)用程序,但我更喜歡將其綁定到客戶端,因為您已經(jīng)從接口和調(diào)用上獲得了靜態(tài)反饋)。最后,正如@Markus 指出的那樣,從集成測試到 e2e 測試的步驟非常小,并且允許您測試您的全部服務(wù)。

TA貢獻1895條經(jīng)驗 獲得超3個贊
根據(jù)其定義,如果您使用外部資源測試您的集成,我們談?wù)摰氖羌蓽y試,而不是單元測試。所以我們這里有兩個問題要解決。
單元測試
你通常做的是擁有一個接受接口的數(shù)據(jù)訪問層,這反過來又很容易模擬,你可以對你的應(yīng)用程序邏輯進行單元測試。
package main
import (
"errors"
"fmt"
)
var (
values = map[string]string{"foo": "bar", "bar": "baz"}
Expected = errors.New("Expected error")
)
type Getter interface {
Get(name string) (string, error)
}
// ErrorGetter implements Getter and always returns an error to test the error handling code of the caller.
// ofc, you could (and prolly should) use some mocking here in order to be able to test various other cases
type ErrorGetter struct{}
func (e ErrorGetter) Get(name string) (string, error) {
return "", Expected
}
// MapGetter implements Getter and uses a map as its datasource.
// Here you can see that you actually get an advantage: you decouple your logic from the data source,
// making refactoring (and debugging) **much** easier WTSHTF.
type MapGetter struct {
data map[string]string
}
func (m MapGetter) Get(name string) (string, error) {
if v, ok := m.data[name]; ok {
return v, nil
}
return "", fmt.Errorf("No value found for %s", name)
}
type retriever struct {
g Getter
}
func (r retriever) retrieve(name string) (string, error) {
return r.g.Get(name)
}
func main() {
// Assume this is test code. No tests possible on playground ;)
bad := retriever{g: ErrorGetter{}}
s, err := bad.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
// Needs to fail as well, as "baz" is not in values
good := retriever{g: MapGetter{values}}
s, err = good.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
s, err = good.retrieve("foo")
if s != "bar" || err != nil {
panic("Something went seriously wrong")
}
}
在上面的例子中,我實際上必須實現(xiàn)兩個 Getter 來覆蓋所有測試用例,因為我不能使用模擬庫,但你明白了。
至于過度工程:簡單明了,不,這不是過度工程。這就是我個人所說的正確工藝。從長遠來看,習(xí)慣它會付出代價。也許不在這個項目中,但在未來的一個項目中。
集成測試
狡猾。我傾向于做的是在我提交查詢之前確保我的查詢是正確的;)
在極少數(shù)情況下,我真的想在 CI 中驗證我的查詢,例如,我通常會創(chuàng)建一個 Makefile,它會啟動一個 docker(-compose),它提供我想要集成的東西,然后運行測試。
- 2 回答
- 0 關(guān)注
- 193 瀏覽
添加回答
舉報