Skip to main content
hitspec has first-class support for GraphQL. Instead of manually constructing JSON bodies, you write queries in a >>>graphql block and variables in a >>>variables block. hitspec handles the rest.

Prerequisites

The Complete Test File

This example tests the Countries GraphQL API, a free public GraphQL endpoint. Create a file called graphql.http:
graphql.http
@baseUrl = https://countries.trevorblades.com

### Get a country by code
# @name getCountry
# @tags smoke

POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query GetCountry($code: ID!) {
  country(code: $code) {
    name
    capital
    currency
    emoji
    languages {
      name
      code
    }
    continent {
      name
    }
  }
}

>>>variables
{
  "code": "US"
}
<<<

>>>
expect status 200
expect body.data.country.name == "United States"
expect body.data.country.capital == "Washington D.C."
expect body.data.country.currency contains "USD"
expect body.data.country.emoji exists
expect body.data.country.languages type array
expect body.data.country.languages length > 0
expect body.data.country.languages[0].name == "English"
expect body.data.country.continent.name == "North America"
<<<

>>>capture
countryName from body.data.country.name
continentName from body.data.country.continent.name
<<<

### List all continents
# @name listContinents
# @tags smoke

POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query {
  continents {
    code
    name
  }
}
<<<

>>>
expect status 200
expect body.data.continents type array
expect body.data.continents length 7
expect body.data.continents[0].code exists
expect body.data.continents[0].name exists
<<<

### Get countries in a continent
# @name countriesInContinent
# @tags continent
# @depends getCountry

POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query GetContinent($code: ID!) {
  continent(code: $code) {
    name
    countries {
      name
      capital
      emoji
    }
  }
}

>>>variables
{
  "code": "NA"
}
<<<

>>>
expect status 200
expect body.data.continent.name == "North America"
expect body.data.continent.countries type array
expect body.data.continent.countries length > 0
<<<

>>>capture
countryCount from body.data.continent.countries.#
<<<

### Get languages
# @name getLanguages
# @tags language

POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query GetLanguage($code: ID!) {
  language(code: $code) {
    name
    native
    rtl
  }
}

>>>variables
{
  "code": "en"
}
<<<

>>>
expect status 200
expect body.data.language.name == "English"
expect body.data.language.native == "English"
expect body.data.language.rtl == false
<<<

### Query with multiple filters
# @name filteredCountries
# @tags filter

POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query FilterCountries($filter: CountryFilterInput) {
  countries(filter: $filter) {
    name
    code
    capital
    continent {
      name
    }
  }
}

>>>variables
{
  "filter": {
    "continent": {
      "eq": "NA"
    }
  }
}
<<<

>>>
expect status 200
expect body.data.countries type array
expect body.data.countries length > 0
expect body.data.countries each type object
<<<

Step-by-Step Breakdown

1

Write a GraphQL query with variables

The >>>graphql block contains the query, and >>>variables provides the variable values as JSON.
POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query GetCountry($code: ID!) {
  country(code: $code) {
    name
    capital
    currency
  }
}

>>>variables
{
  "code": "US"
}
<<<
hitspec converts this into the standard GraphQL JSON format:
{
  "query": "query GetCountry($code: ID!) { country(code: $code) { name capital currency } }",
  "variables": { "code": "US" }
}
You write the query naturally and hitspec handles the serialization.
2

Assert nested response fields

GraphQL responses have a data wrapper. Use dot notation to reach nested fields.
>>>
expect status 200
expect body.data.country.name == "United States"
expect body.data.country.capital == "Washington D.C."
expect body.data.country.currency contains "USD"
expect body.data.country.languages type array
expect body.data.country.languages[0].name == "English"
expect body.data.country.continent.name == "North America"
<<<
The JSON path syntax supports:
  • Dot notation: body.data.country.name
  • Array indexing: body.data.country.languages[0].name
  • Length via gjson: body.data.country.languages.#
3

Capture values from GraphQL responses

Captures work the same way as with REST responses. Extract values for use in later requests.
>>>capture
countryName from body.data.country.name
continentName from body.data.country.continent.name
<<<
These values are accessible as {{getCountry.countryName}} and {{getCountry.continentName}} in subsequent requests.
4

Query without variables

For queries with no variables, you can omit the >>>variables block.
POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query {
  continents {
    code
    name
  }
}
<<<
5

Use the each operator for collections

The each operator applies an assertion to every element in an array.
>>>
expect status 200
expect body.data.countries type array
expect body.data.countries each type object
<<<
This verifies that every item in the countries array is a JSON object.
6

Run the GraphQL tests

hitspec run graphql.http
Run only smoke-tagged queries:
hitspec run graphql.http --tags smoke
Expected output:
  getCountry (312ms)
  listContinents (198ms)
  countriesInContinent (245ms)
  getLanguages (167ms)
  filteredCountries (223ms)

5 passed, 0 failed

GraphQL Error Handling

GraphQL APIs return errors in a data.errors array. You can assert on these:
POST {{baseUrl}}/
Content-Type: application/json

>>>graphql
query {
  nonExistentField {
    id
  }
}
<<<

>>>
expect status 200
expect body.errors exists
expect body.errors type array
expect body.errors length > 0
expect body.errors[0].message exists
<<<
GraphQL APIs typically return HTTP 200 even for query errors. Always check body.errors in addition to the status code.

Next Steps