🧪

RSpecでGraphQLのResolversのテストの書き方

2024/07/28に公開

以前は下記のようなResolversとそれに対するテストを書いていました(書いてありました)。
コードはちょっとだけ変えてあります。

module Resolvers
  class Users < Resolvers::Base
    type ObjectTypes::User.connection_type, null: false

    description 'Find users by roles. You can pass XXX_role or [XXX_role, YYY_role].'

    argument :role, [EnumTypes::User::Role]

    def resolve(role:)
      if role&.include?('evaluation_role')
        User.evaluation_users
      elsif role&.include?('level_aptitude_role')
        User.level_aptitude_users
      else
        User.where(role: role)
      end
    end
  end
end
require 'rails_helper'

RSpec.describe Resolvers::Users do
  describe '#resolve' do
    context 'when role includes evaluation_role' do
      it 'returns evaluation test users filtered by service' do
        user1 = create(:user, role: 'evaluation_role')
        user2 = create(:user, role: 'evaluation_role')
        allow(User).to receive(:evaluation_test_users).and_return([user1, user2])

        result = described_class.new(object: nil, field: nil, context: nil).resolve(role: ['evaluation_role'])

        expect(result).to contain_exactly(user1, user2)
      end
    end
  end
end

graphql-ruby のバージョンを 2.3.7 から 2.3.10 に上げたところ、undefined method types' for nil:NilClass` のエラーとなるようになりました。

こちらの変更は下記の変更によるもので、ISSUEを起票して作者の方に問い合わせをしたところ、ResolversはGraphQLクエリ以外ではサポートされていませんとのことです。

https://github.com/rmosolgo/graphql-ruby/commit/db7a9bcaceb77dc0af78b73e0d5c2ad849c27598#diff-fdbf285d34242cb3cb7b1e56e80f5d39bc600246e72b7bb32190e555432f156cR39

代わりに教えていただいた代替策は下記の2点です。

  • 完全なGraphQLコンテキストを提供するrun_graphql_fieldヘルパーを使用すること
  • または、ダミーのQuery::Contextインスタンスを作成して、context: nilの代わりにわたすことこれも
    • GraphQL-RubyのパブリックAPIの範囲外だが、しばらくは使えるはずとのこと
query_ctx = GraphQL::Query.new(MySchema, "{ __typename }").context

今回は前者の方で対応しましたが、両方のサンプルコードをのせておきます。

require 'rails_helper'

RSpec.describe Resolvers::Users, type: :graphql do
  describe '#resolve' do
    subject { run_graphql_field("Query.users", nil, arguments: { role: role }).items }

    let!(:user1) { create(:user, role: 'evaluation_role') }
    let!(:user2) { create(:user, role: 'evaluation_role') }

    context 'when role includes evaluation_role' do
      let(:role) { 'evaluation_role' }

      it {
        expect(subject).to eq [user2, user2]
      }
    end
  end
end
require 'rails_helper'

RSpec.describe Resolvers::Users do
  describe '#resolve' do
    subject { resolver.resolve(role: role) }

    let(:query_ctx) { GraphQL::Query.new(TofflerSchema, "{ __typename }").context }
    let(:resolver) { described_class.new(object: nil, context: query_ctx, field: nil) }
    let!(:user1) { create(:user, role: 'evaluation_role') }
    let!(:user2) { create(:user, role: 'evaluation_role') }

    context 'when role includes evaluation_role' do
      let(:role) { 'evaluation_role' }

      it {
        expect(subject).to eq [user2, user1]
      }
    end
  end
end

なおHelperの設定は下記のようにしています。

 config.with_options type: :graphql do |graphql_config|
    graphql_config.include GraphQL::Testing::Helpers.for(MySchema)
  end

こちらのやりとりのISSUEは下記です。
ref. https://github.com/rmosolgo/graphql-ruby/issues/5040

私達は今回、1つ目のrun_graphql_fieldを使う方法を取りました。ダミーのQuery::Contextインスタンスを使う方法は既存のコードベースの変更が少なくてよいのですが、パブリックAPIではないということで今後メンテナンスを考えるとrun_graphql_fieldを使っておいたほうが良いと判断をしました。

Discussion