Nolan Phillips
Nolan Phillips's Blog

Nolan Phillips's Blog

Testing GraphQL in Rails

Writing clean RSpec tests with Rails and graphql-ruby.

Nolan Phillips's photo
Nolan Phillips
·Apr 13, 2022·

3 min read

Table of contents

  • Goals
  • Writing Queries
  • What are we testing?
  • Define the Query
  • Testing the logged-out state
  • Testing the logged-in state
  • Review

Goals

When we set out to test our GraphQL API we wanted 3 things:

  1. To write GQL queries as a string
  2. To keep network details out of spec files
  3. To keep GQL queries out of individual tests

Assumptions: This article assumes you have a working Rails app with authentication and GraphQL already setup. The examples below are written with devise, rspec, factory-bot, and graphql-ruby.

Writing Queries

We needed some help. We want to write our queries as strings, and we don't want our specs to know about network requests. To take care of that, we create a helper method that we will include in all of our Specs.

spec/graphql_helper.rb

module GraphqlHelper
  def gql(query)
    body = { query: query }

    post '/api/graphql', params: body

    JSON.parse(response.body)
  end
end

RSpec let's us add modules at configuration time:

spec/rails_helper.rb

RSpec.configure do |config|
   config.include GraphqlHelper
   # ...
end

What are we testing?

We're going to write a couple test for querying the currently logged in user.

spec/graph/current_user_spec.rb

require 'rails_helper'

describe 'Current User Graph', type: :request do
  it 'is nil when not logged in'
  it 'is the signed in user'
end

Note: Make sure to add type: request to the describe block.

Define the Query

Adding a query method to the test-class helps keep the individual specs clean. Let's add it as a private method so it's clearly separated from the specs:

spec/graph/current_user_spec.rb

describe 'Current User Graph', type: :request do
  it 'is nil when not logged in'
  it 'is the signed in user'

  private

  def query_user
    response = gql <<-GQL
      query CurrentUserSpec {
        user {
          firstName
          lastName
        }
      }
    GQL

    response.dig('data', 'user')
  end
end

It's often useful to dig the value you're interested in out of the response. This really makes checking the data easier.

Testing the logged-out state

There is no user when we're logged out, so we need to test for that. Thanks to our helpers it is a very small test:

spec/graph/current_user.rb

describe 'Current User Graph', type: :request do
  it 'is nil when not logged in' do
    user_data = query_user

    expect(user_data).to be_nil
  end

  #...
end

Testing the logged-in state

spec/graph/current_user_spec.rb

describe 'Current User Graph', type: :request do
  # ...

  it 'is the signed in user' do
    user = FactoryBot.create(:user)
    sign_in user

    user_data = query_user

    expect(user_data['firstName']).to eq(user.first_name)
    expect(user_data['lastName']).to eq(user.last_name)
  end
end

Review

Here is the end-state of our current_user_spec.

require 'rails_helper'

describe 'Current User Graph', type: :request do
  it 'is nil when not logged in' do
    user_data = query_user

    expect(user_data).to be_nil
  end

  it 'is the signed in user' do
    user = FactoryBot.create(:user)
    sign_in user

    user_data = query_user

    expect(user_data['firstName']).to eq(user.first_name)
    expect(user_data['lastName']).to eq(user.last_name)
  end

  private

  def query_user
    response = gql <<-GQL
      query CurrentUserSpec {
        user {
          firstName
          lastName
        }
      }
    GQL

    response.dig('data', 'user')
  end
end
 
Share this