自动测试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服务器的测试。