Ваши тестовые случаи не должны превышать go за пределами реализации. Я специально имею в виду пустой ввод-непустой ввод или любой другой тип ввода.
Давайте посмотрим на код, который вы хотите проверить:
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
}
Ничего в Реализация выше делает что-либо на основе значения параметра id
, и поэтому ничто в ваших тестах для этого фрагмента кода не должно заботиться о том, что вводится, если оно не имеет отношения к реализации, оно также не должно относиться к тестам.
Ваш GetPost
имеет в основном две ветви кода, которые берутся на основе одного фактора, то есть "ноль" возвращенной переменной err
. Это означает, что для вашей реализации есть только два возможных результата, основанных на том, что возвращает err
значение Run
, и, следовательно, должно быть только два тестовых случая, 3-й или 4-й тестовый случай будет просто вариацией если не прямая копия первых двух.
Ваш тестовый клиент также делает некоторые ненужные вещи, основным из которых является его имя, т.е. это не полезно Обычно mocks делает намного больше, чем просто возвращает предопределенные значения, они гарантируют, что методы вызываются в ожидаемом порядке и с ожидаемыми аргументами и т. Д. c. И на самом деле здесь вам вообще не нужно издеваться, так что хорошо, что это не так.
Имея это в виду, вот что я бы посоветовал вам сделать с вашим тестовым клиентом.
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)
}
})
}
}
... здесь происходит небольшое повторение, необходимость разобрать post
и err
дважды, но это небольшая цена, которую нужно заплатить по сравнению с более сложной / сложной тестовой установкой, которая заполняет тестовый клиент из ожидаемых выходных полей тестового примера.
Добавление :
Если бы вы обновили GetPost
таким образом, чтобы он проверял пустой идентификатор и возвращал ошибку перед отправкой запроса в graphql, тогда ваша первоначальная настройка имела бы гораздо больше смысла:
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
}
... и соответственно обновлять тестовые наборы:
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)
}
})
}
}