首页 > 后端开发 > Golang > 正文

如何使用 Testify 和 gqlgen/client 在 Go 中对 GraphQL 解析器进行单元测试?

Patricia Arquette
发布: 2024-10-28 05:00:01
原创
560 人浏览过

How to Unit Test GraphQL Resolvers in Go with Testify and gqlgen/client?

使用 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 &amp;mutationResolver{r} }

// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &amp;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: &amp;resolvers})))
        ue := model.UserEntity{ID: "123", User: model.User{Loginname: &amp;loginname, AvatarURL: &amp;avatarURL}}
        testUserService.On("ValidateAccessToken", mock.AnythingOfType("string")).Return(&amp;ue)
        var resp struct {
            ValidateAccessToken struct{ ID, Loginname, AvatarUrl string }
        }
        q := `
      mutation { 
        validateAccessToken(accesstoken: "abc") { 
          id, 
          loginname, 
          avatarUrl 
        } 
      }
    `
        c.MustPost(q, &amp;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: &amp;resolvers})))
        u := model.UserDetail{User: model.User{Loginname: &amp;loginname, AvatarURL: &amp;avatarURL}, Score: &amp;score, CreateAt: &amp;createAt}
        testUserService.On("GetUserByLoginname", mock.AnythingOfType("string")).Return(&amp;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, &amp;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中文网其他相关文章!

来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!