golang中的Mock依赖
Mock依賴
有的時(shí)候,由于業(yè)務(wù)邏輯的復(fù)雜性,功能代碼并不會(huì)就這么直接,往往還會(huì)摻雜很多其他組件,這就給我們的測(cè)試工作帶來很大的麻煩,我這里列舉幾個(gè)常見的依賴:
- 組件依賴
- 函數(shù)依賴
組件依賴和函數(shù)依賴是兩種比較常見的依賴,但是,這兩種依賴也是可以擴(kuò)展開來說的,既可能來自于我們自己編寫的組件/函數(shù),也可能是引入其他人寫的。但是,無妨,對(duì)于這些情況,我們都會(huì)做一些分析
組件依賴處理
傳一個(gè) Stub 組件進(jìn)入,從而達(dá)到控制依賴組件行為的效果
舉一個(gè)例子先,例如我們比較常見的 Service 層和 DAO 層的操作,Service 處理完邏輯之后,交給 DAO 層進(jìn)行持久化,或者需要調(diào)用 DAO 層從持久化中獲取一些必要的數(shù)據(jù);在測(cè)試的時(shí)候,我們很多時(shí)候不希望真的持久化或者從持久化中獲取數(shù)據(jù),那么就會(huì)對(duì) DAO 層進(jìn)行一些 Mock
import "fmt"type Data struct {Field string }type Dao interface {ReadAll() []DataSaveData(d *Data) }type MongoDao struct {}func (d MongoDao) ReadAll() []Data {return []Data{} }func (d MongoDao) SaveData(data *Data) {//... }type Service struct {Dao *Dao }func (s *Service) Login (username string) bool {users := (*s.Dao).ReadAll()for _, user := range users {if username == user.Field {return true}}return false }func Newservice(d Dao) *Service {srv := Service{Dao: &d}return &srv }func main() {d := MongoDao{}srv := Newservice(d)fmt.Println(srv.Login("abc")) }這里我們想要測(cè)試Service的正確性,但是又不想要真的持久化 DAO,所以,這個(gè)時(shí)候我們會(huì)自己創(chuàng)建一個(gè) Stub,然后提供給 Service,同時(shí),我們還能操作 DAO 的行為,達(dá)到運(yùn)行得效果
//用StubDao代替Mongodb type StubDao struct {}func (d StubDao) ReadAll() []Data {return []Data{Data{"abc"}} }func (d StubDao) SaveData(data *Data) { }func TestLogin(t *testing.T) {d := StubDao{}srv := NewService(d)rst := srv.Login("abc")if !rst {t.Error("login error")} }這里對(duì)測(cè)試代碼稍微改了一下,可以發(fā)現(xiàn),我們可以通過修改一個(gè)變量來控制 Stub 的輸出,從而達(dá)到測(cè)試不同功能的效果,這就解決了組件依賴的問題
函數(shù)依賴
函數(shù)依賴相比于組件依賴會(huì)更麻煩一點(diǎn),因?yàn)槲覀冊(cè)谇懊婵梢钥吹?#xff0c;組件依賴的話我們可以傳遞 Stub 進(jìn)行,這樣我們可以隨意得控制 Stub 的行為,但是函數(shù)不行呀,這里我們又不能傳函數(shù)進(jìn)去,因?yàn)楹瘮?shù)是被 import 進(jìn)去的啊。問題就在這了,因?yàn)楹瘮?shù)是被 import 進(jìn)去的,所以可以理解為函數(shù)是全局的了,既然這樣,那么我們?yōu)槭裁床恍薷囊幌潞瘮?shù)呢?什么意思?我們先來看著正常的業(yè)務(wù)例子
var Login = func(username, password string) bool {if username == password {return false}return true }func Reply(username, password, msg string) bool {if Login(username, password) {fmt.Println(msg)return true}return false }func stu() {Reply("a", "b", "aa")Reply("a", "b", "bb") }要先登錄,然后登錄完之后我們才能回復(fù)消息,這里我們的登錄邏輯是簡單的,但是,在實(shí)際業(yè)務(wù)中可能這里的登錄邏輯就設(shè)計(jì)到 DB 訪問等等,我們希望不走真實(shí)的邏輯,而是自己來控制Login的行為
先分析一下我們的 UT 目的,我們的目的是測(cè)試Reply函數(shù),我們期望是Login成功,那么Reply也應(yīng)該是成功的;如果Login失敗,那么Reply也應(yīng)該是失敗的。這個(gè)測(cè)試結(jié)論不應(yīng)該被Login所影響,及時(shí)以后Login邏輯修改了,我們也應(yīng)該是這個(gè)邏輯,不會(huì)受到影響,那么我們可以這么編寫 UT
func TestSuccReply(t *testing.T) {origLogin := Logindefer func() {Login = origLogin}()Login = func(username, password string) bool {return true}if !Reply("a", "a", "aaa") {t.Errorf("reply false for login success")} }func TestLogin(t *testing.T) {origLogin := Logindefer func() {Login = origLogin}()Login = func(username, password string) bool {return false}if Reply("a", "a", "aa") {t.Errorf("reply true for login fail")} }這里可以發(fā)現(xiàn),我們是修改了Login這個(gè)函數(shù)的代碼,從而控制Login函數(shù)的返回值,這樣我們就可以測(cè)試我們寫的代碼的邏輯是否正確了
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的golang中的Mock依赖的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang中的delve
- 下一篇: golang中的new和make的区别