自动测试GraphQL查询

你好。我是在Bit Journey公司担任工程师的@pocke。
在Bit Journey,我们正在开发名为Kibela的Web应用程序。

Kibela使用GraphQL提供了一个公共的Web API。
https://github.com/kibela/kibela-api-v1-document
此外,该API也被用于Kibela的Web应用程序内部。

最近,我发布了一个用于自动(部分地)全面测试该GraphQL API的Gem包– graphql-autotest。
在本文中,我将介绍这个graphql-autotest。

GraphQL-autotest虽然是用Ruby编写的,但是可以测试任何语言实现的GraphQL服务器(需要编写一些Ruby代码)。

「網羅性測試」的意思是

graphql-autotest的目标是在一定程度上对GraphQL查询进行全面的测试。

我们在开发GraphQL API时通常会编写单元测试。
理想情况下,我们希望为所有字段编写单元测试,但往往会漏掉一些。

graphql-autotest是一种测试工具,可以捕捉到此类漏洞。
首先,它会自动生成一个从模式定义中获取尽可能多字段的查询。
然后执行该查询,并确认没有错误发生。

重点是自动编写查询。
通过自动化生成查询,即使添加了新字段却忘记编写单元测试,也可以使用graphql-autotest测试该字段。

然而,默认情况下,graphql-autotest仅检查查询是否会产生错误。
它可以检测到有关GraphQL服务器实现中存在的拼写错误导致NoMethodError的情况,但无法检测到返回2而不是1的错误。
然而,如果针对执行查询的响应编写适当的代码进行验证,一定程度上可以进行测试。

另外,只有当使用graphql宝石进行查询时,才能执行查询。如果GraphQL服务器是在Ruby之外的语言中实现的,则需要单独实现执行生成的查询的部分。

使用方法

让我们看一下如何使用。

安装

首先安装这个gem。

$ gem install graphql-autotest

如果您正在使用Bundler,将以下代码添加到Gemfile中。

gem 'graphql-autotest'

设定示例

如果使用了graphql gem

首先,我将介绍使用graphql gem的最简单代码。

require 'graphql/autotest'

class YourSchema < GraphQL::Schema
end

runner = GraphQL::Autotest::Runner.new(
  schema: YourSchema,
  context: { current_user: User.first },
)

runner.report!

首先,我们正在定义一个名为YourSchema的模式。请将其替换为您自己的模式定义。

然后,创建GraphQL::Autotest::Runner的实例,并通过调用report!来生成和执行查询。
将YourSchema和context作为Runner.new的参数传递。当调用YourSchema.execute时,会传递这个上下文。

只需从模式定义中自动生成查询,执行并进行测试以确保没有错误。
然而,在实际应用中,默认设置可能无法获得充分的结果。因此,需要进行配置,如后续所述的arguments_fetcher等设置。

如果不使用graphql gem

正如开头所解释的那样,除了使用graphql gem实现服务器的情况外,这个gem还可以自动生成查询(无法执行查询)。

如果要自动生成查询,请确保有类似于https://github.com/kibela/kibela-api-v1-document/blob/master/schema.graphql的模式定义。
如果该模式定义保存在path/to/schema.graphql中,您可以按照以下方式编写以生成查询。

require 'graphql/autotest'

fields = GraphQL::Autotest::QueryGenerator.from_file(path: 'path/to/schema.graphql')

# 生成したクエリがすべて表示される
fields.each do |field|
  puts field.to_query
end

将生成的查询传递给合适的GraphQL客户端并执行将是一个不错的选择。

设置步骤

在这个阶段,我们已经解释了使用默认设置生成和执行查询的方法。
但正如前面所提到的,使用默认设置可能不足够(比如在Kibela的情况下)。
因此,graphql-autotest提供了一些配置选项。

我将解释以下的设置选项。

参数提取器 shū tí qǔ qì)

在arguments_fetcher中,定义了当字段接收参数时传递哪些参数。
arguments_fetcher指定了一个Proc。指定的Proc会被调用,并且将字段的信息作为参数传入,如果返回一个Hash,则该Hash将被用作参数。

在graphql-autotest中,默认情况下只会将不需要必填参数(即可以在不传参的情况下调用)的字段作为查询输出。
因此,为了获取需要必填参数的字段,可以使用arguments_fetcher来定义应该传递哪些参数。
同时,即使是非必填参数,也可以根据需要进行指定。

例如,在Kibela中,对于Relay Connection必须指定first或last参数。
填充first的fetch_arguments定义如下:

fill_first = proc do |field|
  field.arguments.any? { |arg| arg.name == 'first' } && { first: 5 }
end

fields = GraphQL::Autotest::QueryGenerator.from_file(
  path: 'path/to/schema.graphql',
  arguments_fetcher: GraphQL::Autotest::ArgumentsFetcher.combine(
    fill_first,
    GraphQL::Autotest::ArgumentsFetcher::DEFAULT,
  ),
)

通过更加设定这个arguments_fetcher,可以提高字段的覆盖度。

跳过如果 guò

在skip_if中,您可以跳过不想获取的字段。
它可以用于忽略在自动生成查询时可能导致错误的字段。

如果提供了skip_if的话,可以指定一个Proc。这个Proc会以字段的信息作为参数被调用,如果返回一个true值,那么该字段将被跳过。

skip_if = proc do |field|
  field.name == 'sensitiveField'
end

fields = GraphQL::Autotest::QueryGenerator.from_file(
  path: 'path/to/schema.graphql',
  skip_if: skip_if,
)

最大深度

max_depth用于指定查询的最大深度。

在 graphql-autotest 中,如果存在循环引用的字段,会进行控制,以防止进入无限循环,因此在理论上,即使不指定 max_depth ,查询的执行也会结束。不过如果查询过于深度,则会导致查询的生成和执行耗时较长。这种情况下,请使用 max_depth 选项来减小深度。

默认的max_depth是10。这个值是合适的。

fields = GraphQL::Autotest::QueryGenerator.from_file(
  path: 'path/to/schema.graphql',
  max_depth: 5,
)

总结

我介绍了一个用于执行全面测试GraphQL的Gem,名为graphql-autotest。

在Kibela的实施过程中,我们实际上发现了多个bug。这些bug出现在Kibela的Web应用程序中未使用的字段上。然而,考虑到这些字段可能在向用户公开的API中被获取,我们能够及早发现这些bug是很好的事情。

希望您能够使用graphql-autotest来进行GraphQL服务器的测试。

广告
将在 10 秒后关闭
bannerAds