使用 testify 在 Go 中对 GraphQL 进行单元测试
在 GraphQL 模式中,Query 结构定义触发数据检索和 Mutation 结构的操作定义修改状态的操作。测试时,您需要模拟执行实际 CRUD 操作的对象,并断言进行了预期的调用。
对于以下示例,我们将使用 testify 和 gqlgen/client 包:
graph/schema.graphql
type Query { user(loginname: String!): UserDetail } type Mutation { validateAccessToken(accesstoken: String!): UserEntity } type User { loginname: String avatarUrl: String } type UserEntity { id: ID! loginname: String avatarUrl: String } type UserDetail { loginname: String avatarUrl: String githubUsername: String createAt: String score: Int }
graph/resolver/resolver.go
<code class="go">package resolver import ( "github.com/mrdulin/gqlgen-cnode/services" ) type Resolver struct { UserService services.UserService }</code>
services/user.go
<code class="go">package services type UserService interface { GetUserByLoginname(loginname string) *model.UserDetail ValidateAccessToken(accesstoken string) *model.UserEntity }</code>
graph/resolver/root.resolver.go
<code class="go">package resolver import ( "context" "github.com/mrdulin/gqlgen-cnode/graph/generated" "github.com/mrdulin/gqlgen-cnode/graph/model" ) func (r *mutationResolver) ValidateAccessToken(ctx context.Context, accesstoken string) (*model.UserEntity, error) { return r.UserService.ValidateAccessToken(accesstoken), nil } func (r *queryResolver) User(ctx context.Context, loginname string) (*model.UserDetail, error) { return r.UserService.GetUserByLoginname(loginname), nil } // Mutation returns generated.MutationResolver implementation. func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver }</code>
为了模拟 GraphQL 请求和响应,我们可以为服务创建模拟对象:
mocks/userService.go
<code class="go">package mocks import ( "github.com/mrdulin/gqlgen-cnode/graph/model" "github.com/stretchr/testify/mock" ) type MockedUserService struct { mock.Mock } func (s *MockedUserService) GetUserByLoginname(loginname string) *model.UserDetail { args := s.Called(loginname) return args.Get(0).(*model.UserDetail) } func (s *MockedUserService) ValidateAccessToken(accesstoken string) *model.UserEntity { args := s.Called(accesstoken) return args.Get(0).(*model.UserEntity) }</code>
最后,在我们的测试文件中,我们可以导入这些模拟并使用它们测试我们的 GraphQL 解析器:
graph/resolver/root.resolver_test.go
<code class="go">package resolver_test import ( "testing" "github.com/99designs/gqlgen/client" "github.com/99designs/gqlgen/graphql/handler" "github.com/mrdulin/gqlgen-cnode/graph/generated" "github.com/mrdulin/gqlgen-cnode/graph/model" "github.com/mrdulin/gqlgen-cnode/graph/resolver" "github.com/mrdulin/gqlgen-cnode/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) var ( loginname = "mrdulin" avatarURL = "avatar.jpg" score = 50 createAt = "1900-01-01" ) func TestMutationResolver_ValidateAccessToken(t *testing.T) { t.Run("should validate accesstoken correctly", func(t *testing.T) { testUserService := new(mocks.MockedUserService) resolvers := resolver.Resolver{UserService: testUserService} c := client.New(handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers}))) ue := model.UserEntity{ID: "123", User: model.User{Loginname: &loginname, AvatarURL: &avatarURL}} testUserService.On("ValidateAccessToken", mock.AnythingOfType("string")).Return(&ue) var resp struct { ValidateAccessToken struct{ ID, Loginname, AvatarUrl string } } q := ` mutation { validateAccessToken(accesstoken: "abc") { id, loginname, avatarUrl } } ` c.MustPost(q, &resp) testUserService.AssertExpectations(t) require.Equal(t, "123", resp.ValidateAccessToken.ID) require.Equal(t, "mrdulin", resp.ValidateAccessToken.Loginname) require.Equal(t, "avatar.jpg", resp.ValidateAccessToken.AvatarUrl) }) } func TestQueryResolver_User(t *testing.T) { t.Run("should query user correctly", func(t *testing.T) { testUserService := new(mocks.MockedUserService) resolvers := resolver.Resolver{UserService: testUserService} c := client.New(handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers}))) u := model.UserDetail{User: model.User{Loginname: &loginname, AvatarURL: &avatarURL}, Score: &score, CreateAt: &createAt} testUserService.On("GetUserByLoginname", mock.AnythingOfType("string")).Return(&u) var resp struct { User struct { Loginname, AvatarURL, CreateAt string Score int } } q := ` query GetUser($loginname: String!) { user(loginname: $loginname) { loginname avatarUrl createAt score } } ` c.MustPost(q, &resp, client.Var("loginname", "mrdulin")) testUserService.AssertCalled(t, "GetUserByLoginname", "mrdulin") require.Equal(t, "mrdulin", resp.User.Loginname) require.Equal(t, "avatar.jpg", resp.User.AvatarURL) require.Equal(t, 50, resp.User.Score) require.Equal(t, "1900-01-01", resp.User.CreateAt) }) }</code>
此测试将确保对服务方法进行正确的调用,并且响应的格式符合预期。测试覆盖率报告应显示解析器功能的完整覆盖率。
以上是如何使用 Testify 和 gqlgen/client 在 Go 中对 GraphQL 解析器进行单元测试?的详细内容。更多信息请关注PHP中文网其他相关文章!