1 回答

TA貢獻1921條經(jīng)驗 獲得超9個贊
您的測試用例不應(yīng)超出實現(xiàn)范圍。我特別指的是空對非空輸入或任何類型的輸入。
讓我們看一下您要測試的代碼:
func (c *Client) GetPost(id string) (*Post, error) {
req := graphql.NewRequest(`
query($id: String!) {
getPost(id: $id) {
id
title
}
}
`)
req.Var("id", id)
var resp getPostResponse
if err := c.gcl.Run(ctx, req, &resp); err != nil {
return nil, err
}
return resp.Post, nil
}
上面的實現(xiàn)中沒有任何東西基于id參數(shù)值做任何事情,因此在你的這段代碼的測試中沒有任何東西應(yīng)該真正關(guān)心傳入的輸入,如果它與實現(xiàn)無關(guān),它也應(yīng)該與測試無關(guān)。
您GetPost基本上有兩個基于單個因素的代碼分支,即返回err變量的“零”。這意味著就您的實現(xiàn)而言,根據(jù)返回的err值,只有兩種可能的結(jié)果,Run因此應(yīng)該只有兩個測試用例,第三個或第四個測試用例只是一個變體,如果不是一個完整的副本, 前兩個。
你的測試客戶端也在做一些不必要的事情,主要的是它的名字,即你所擁有的不是一個模擬,所以調(diào)用它是沒有幫助的。模擬通常不僅僅是返回預(yù)定義的值,它們確保方法被調(diào)用,以預(yù)期的順序和預(yù)期的參數(shù)等。實際上你根本不需要模擬,所以這是一件好事'語氣。
考慮到這一點,我建議您對測試客戶端執(zhí)行以下操作。
type testGraphqlClient struct {
resp interface{} // non-pointer value of the desired response, or nil
err error // the error to be returned by Run, or nil
}
func (g testGraphqlClient) Run(_ context.Context, _ *graphql.Request, resp interface{}) error {
if g.err != nil {
return g.err
}
if g.resp != nil {
// use reflection to set the passed in response value
// (i haven't tested this so there may be a bug or two)
reflect.ValueOf(resp).Elem().Set(reflect.ValueOf(g.resp))
}
return nil
}
...這是必要的測試用例,所有兩個:
func TestClient_GetPost(t *testing.T) {
tests := []struct {
name string
post *Post
err error
client testGraphqlClient
}{{
name: "return error from client",
err: errors.New("bad input"),
client: testGraphqlClient{err: errors.New("bad input")},
}, {
name: "return post from client",
post: &Post{id: aws.String("123")},
client: testGraphqlClient{resp: getPostResponse{Post: &Post{id: aws.String("123")}}},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := Client{gql: tt.client}
post, err := client.GetPost("whatever")
if !cmp.Equal(err, tt.err) {
t.Errorf("got error=%v want error=%v", err, tt.err)
}
if !cmp.Equal(post, tt.post) {
t.Errorf("got post=%v want post=%v", post, tt.post)
}
})
}
}
...這里有一些重復(fù),需要拼出postanderr兩次,但與更復(fù)雜/復(fù)雜的測試設(shè)置相比,這是一個很小的代價,它將從測試用例的預(yù)期輸出字段填充測試客戶端.
附錄:
如果您要GetPost以這樣一種方式進行更新,即在向 graphql 發(fā)送請求之前檢查空 id 并返回錯誤,那么您的初始設(shè)置將更有意義:
func (c *Client) GetPost(id string) (*Post, error) {
if id == "" {
return nil, errors.New("empty id")
}
req := graphql.NewRequest(`
query($id: String!) {
getPost(id: $id) {
id
title
}
}
`)
req.Var("id", id)
var resp getPostResponse
if err := c.gcl.Run(ctx, req, &resp); err != nil {
return nil, err
}
return resp.Post, nil
}
...并相應(yīng)地更新測試用例:
func TestClient_GetPost(t *testing.T) {
tests := []struct {
name string
id string
post *Post
err error
client testGraphqlClient
}{{
name: "return empty id error",
id: "",
err: errors.New("empty id"),
client: testGraphqlClient{},
}, {
name: "return error from client",
id: "nonemptyid",
err: errors.New("bad input"),
client: testGraphqlClient{err: errors.New("bad input")},
}, {
name: "return post from client",
id: "nonemptyid",
post: &Post{id: aws.String("123")},
client: testGraphqlClient{resp: getPostResponse{Post: &Post{id: aws.String("123")}}},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := Client{gql: tt.client}
post, err := client.GetPost(tt.id)
if !cmp.Equal(err, tt.err) {
t.Errorf("got error=%v want error=%v", err, tt.err)
}
if !cmp.Equal(post, tt.post) {
t.Errorf("got post=%v want post=%v", post, tt.post)
}
})
}
}
- 1 回答
- 0 關(guān)注
- 105 瀏覽
添加回答
舉報