Grounded Flow 个人成长

Mock in GO

2022-01-25
豆藏

代码中用到了firebase的Authentication,在测试的过程中,需要从Request header里面读取IdToken, 而这个是第三方签发的,无法自己模拟(自己做出来的都是CustomeToken不是一个东西,token的header是不一样的)。现实中也不可能每次跑测试的时候就启动service去搞一个token复制粘贴,就想到了mock。

Golang自己有mock的标准库,但都需要安装CLI,跑一个命令自动生成一些代码。感觉特别麻烦,研究了一下还是决定用Golang interface的强大功能来手动mock。

基本思路是把server的authentication功能分离出来,做成一个interface,在实际使用中使用真正的firebase auth,测试的时候,传入一个假的。

一开始server差不多是这样的。所有的方法都是Server的方法,耦合度很高。

type Server sturct {
config Config
routes *chi.Mux
db *postgres.db
}
// Authentication
func (s *Server) Authenticate (r *http.Request) *auth.Token, error {
	// init firebase app, get client, verifyToken, not runable code
	tokenString = strings.Split(r.Header.Get("Authorization"), "Bearer ")[1]
	token, err := client.VerifyIDToken(ctx, tokenString)
	// check err and return decoded token as IdToken Struct
	return token, err
}

首先,给Server加一个field, 同时定义AuthMethod接口

type Server struct {
	config         Config
	db             *postgres.DB
	router         *chi.Mux
	authentication AuthMethod
}

type AuthMethod interface {
	Authenticate(*http.Request) (*auth.Token, error)
}

type FirebaseAuth struct{}

把原有的Authenticate方法改成新的类FirebaseAuth的方法:

func (s *FirebaseAuth) Authenticate (r *http.Request) *auth.Token, error {...}

在测试文件中定义一个mock类,并实现Authenticate方法:

type MockAuth struct{}

func (m *MockAuth) Authenticate(r *http.Request) (*auth.Token, error) {
	fmt.Println("Mock token authentication")
	return &auth.Token{UID: "test-firebase-uid"}, nil
}

启动一个Server实例时,传入&FirebaseAuth{} 或者&MockAuth{} 即可。

进一步思考,在实际定义struct的时候,层级越高(比如Server),就应该把功能全部分解开,都定义成接口,增加测试的灵活性,降低更改难度。

References

https://medium.com/safetycultureengineering/flexible-mocking-for-testing-in-go-f952869e34f5

https://stackoverflow.com/questions/42839517/how-to-mock-only-specific-method-in-golang


Comments

Content