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:
@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
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. 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.#
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. 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
}
}
<<<
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. Run the GraphQL tests
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