HTTP REQUESTS
GraphQL Testing
Test GraphQL APIs seamlessly with Karate's built-in support for queries, mutations, variables, and powerful JsonPath validation for deeply nested responses.
Why GraphQL with Karate?
- Native text handling: Use the
text
keyword to prevent JSON parsing issues with GraphQL syntax - Dynamic queries: Build queries with placeholders, variables, and conditional fragments
- Powerful validation: JsonPath and fuzzy matching handle complex, nested GraphQL responses effortlessly
- Data-driven testing: Combine with
Scenario Outline
for parameterized query testing
Basic GraphQL Query
Send a simple GraphQL query using the text
keyword:
Feature: Basic GraphQL query
Scenario: Query a single hero
Given url 'https://api.example.com'
And path 'graphql'
And text query = """{ hero { name height mass } }"""
And request { query: '#(query)' }
And header Accept = 'application/json'
When method post
Then status 200
And match response.data.hero.name == '#string'
Use text
instead of def
to prevent Karate from parsing GraphQL syntax as JSON.
GraphQL queries look like JSON but are not valid JSON. The text
keyword tells Karate to treat the content as raw text, avoiding parsing errors. Always use text
for GraphQL queries defined inline.
GraphQL Mutations
Execute mutations to create or update data:
Feature: GraphQL mutations
Scenario: Create a new user
Given url apiUrl
And path 'graphql'
And text mutation =
"""
mutation {
createUser(name: "Alice Johnson", email: "alice@example.com") {
id
name
email
createdAt
}
}
"""
And request { query: '#(mutation)' }
When method post
Then status 200
And match response.data.createUser ==
{
id: '#number',
name: 'Alice Johnson',
email: 'alice@example.com',
createdAt: '#string'
}
Query Variables
Pass dynamic variables to GraphQL queries for reusability:
Feature: GraphQL with variables
Scenario: Query hero by ID
Given url apiUrl
And path 'graphql'
And text query =
"""
query GetHero($heroId: Int!) {
hero(id: $heroId) {
name
height
mass
homeworld
}
}
"""
And def variables = { heroId: 1001 }
And request { query: '#(query)', variables: '#(variables)' }
When method post
Then status 200
And match response.data.hero.name == 'Luke Skywalker'
Scenario: Mutation with variables
Given path 'graphql'
And text mutation =
"""
mutation UpdateUser($id: Int!, $email: String!) {
updateUser(id: $id, email: $email) {
id
email
updatedAt
}
}
"""
And def variables = { id: 123, email: 'newemail@example.com' }
And request { query: '#(mutation)', variables: '#(variables)' }
When method post
Then status 200
And match response.data.updateUser.email == 'newemail@example.com'
File-Based Queries
Organize reusable queries in .graphql
or .gql
files:
Feature: File-based GraphQL queries
Scenario: Load query from file
# File: get-hero.graphql
# query GetHero($id: Int!) {
# hero(id: $id) { name height mass }
# }
Given url apiUrl
And path 'graphql'
And def query = read('get-hero.graphql')
And def variables = { id: 1001 }
And request { query: '#(query)', variables: '#(variables)' }
When method post
Then status 200
And match response.data.hero == { name: '#string', height: '#number', mass: '#number' }
Files with .graphql
and .gql
extensions are automatically treated as text strings.
Organize GraphQL queries in a dedicated folder like src/test/java/queries/
alongside your feature files. Use descriptive names like get-user-by-id.graphql
and create-order-mutation.graphql
for easy maintenance.
Placeholder Substitution
Use placeholders with Scenario Outline
for data-driven GraphQL testing:
Feature: Data-driven GraphQL queries
Scenario Outline: Query heroes by name
Given url apiUrl
And path 'graphql'
# Placeholders use <name> syntax
And text query =
"""
{
hero(name: "<heroName>") {
name
height
mass
}
}
"""
And request { query: '#(query)' }
When method post
Then status 200
And match response.data.hero.name == '<heroName>'
Examples:
| heroName |
| Luke Skywalker |
| Darth Vader |
| Leia Organa |
Dynamic Query Building
Build queries dynamically with the replace
keyword:
Feature: Dynamic query construction
Scenario: Conditionally include fields
Given url apiUrl
And path 'graphql'
And text queryTemplate =
"""
{
hero(id: <heroId>) {
name
<optionalFields>
}
}
"""
# Include different fields based on condition
And def includeDetails = true
And def fields = includeDetails ? 'height
mass
homeworld' : ''
And replace queryTemplate
| token | value |
| heroId | 1001 |
| optionalFields | fields |
And request { query: '#(queryTemplate)' }
When method post
Then status 200
And match response.data.hero.name == '#present'
Response Validation
Validate complex GraphQL responses with JsonPath and fuzzy matching:
Feature: GraphQL response validation
Scenario: Validate nested response structure
Given url apiUrl
And path 'graphql'
And text query =
"""
{
allUsers {
users {
id
name
posts {
title
comments {
author
text
}
}
}
}
}
"""
And request { query: '#(query)' }
When method post
Then status 200
# Validate array sizes
And match response.data.allUsers.users == '#[3]'
# JsonPath for deep queries
And def firstUser = response.data.allUsers.users[0]
And match firstUser.name == '#string'
And match firstUser.posts == '#[_ > 0]'
# Fuzzy matching for dynamic data
And match each response.data.allUsers.users ==
{
id: '#number',
name: '#string',
posts: '#array'
}
# Validate nested comments
And def allComments = karate.jsonPath(response, '$.data.allUsers.users[*].posts[*].comments[*]')
And match allComments == '#[_ > 0]'
And match each allComments == { author: '#string', text: '#string' }
GraphQL Best Practices
Query organization:
- Store reusable queries in
.graphql
files for complex operations - Use inline
text
queries for simple, one-off tests - Group related queries in folders by feature or domain
Variable usage:
- Always use variables for dynamic data (IDs, search terms, filters)
- Define variables in
Background
for scenario reuse - Use fuzzy matching
'#string'
and'#number'
for dynamic response fields
Error handling:
- GraphQL returns
200
even with errors; checkresponse.errors
array - Validate both success and error response structures
- Use
match
for schema validation on error objects
When to use text
vs def
:
- Use
text
for inline GraphQL queries (prevents parsing issues) - Use
def
when reading.graphql
files (already treated as text) - Use
def
for variables objects (standard JSON)
Next Steps
- Validate responses: Response Validation
- Data-driven testing: Data-Driven Tests
- Read query files: Reading Files