How to Structure Graphql Tests in Playwright
- Paul Scholes
- 5 days ago
- 3 min read
Picture this: your frontend is hooked up to a sleek GraphQL API, and your end-to-end tests need to ensure that data flows correctly, not just that UI elements are showing up. But how do you keep things clean, reusable, and reliable when testing GraphQL?
We recently set up GraphQL testing using Playwright and TypeScript, and found a simple structure that kept things readable, efficient, and well-scoped.
Here’s how we approached it.
Separating Concerns: Functions and Queries
It makes sense to start with a well-defined plan for keeping things tidy. Who wants to update every test every time a query changes? No one, other than the slightly insane.
So the best structure looks something like this:
/graphql
└── queries.ts # Reusable GraphQL queries/mutations
/utils
└── graphqlClient.ts # Playwright function to send GraphQL requests
tests
└── graphql.spec.ts # Your actual tests
Nice and tidy, with reusable elements, so a change doesn't have to be hard work. A new data item was added that you need to assert for one of your tests? Just update the central query!
Sending Requests in Playwright
So, how do you send requests in Playright, I imagine I hear you ask? The documentation is here: https://playwright.dev/docs/api/class-request. But to save some reading, here is an example!
// utils/graphqlClient.ts
import { APIRequestContext } from '@playwright/test';
export async function sendGraphQLRequest(
request: APIRequestContext,
query: string,
variables: variables,
operationName: operationName
) {
const response = await request.post('/graphql', {
data: {
operationName = 'operationName',
query,
variables
},
});
const body = await response.json();
return { response, body };
}
So what does this do? You pass the APIRequestContext from your test, along with the query, operation name and variables, and it sends a request. Easy!
Or is it? What are these variables of which we speak (well, type)?
These are the meat of the GraphQL - what it will do. This takes a JSON structure, and here is an example:
const userVariables = {
data: {
firstName: 'Bob',
lastName: 'Bobson',
}
};
So now we have our function to pass the request, and the variables we need. All we need now is the query. And not to break the trend, here is an example of that too!
// graphql/queries.ts
export const CREATE_USERS = `mutation CreateUsers ($data: InputCreateUsers) { users (data: data$) { items { id firstName lastName email } } }`;
To explain this a bit more, the query is a simple string that gives:
The query name
What data is used to drive it - a definition, and where to use it
What fields do we want back in the result
In this case, we have used a mutation, which manipulates data rather than just returning a result, for added spice!
Now we are ready to go - let's put it together in a Playwright test!
// tests/graphql.spec.ts
import { test, expect, request } from '@playwright/test';
import { sendGraphQLRequest } from '../utils/graphqlClient';
import { GET_USERS } from '../graphql/queries';
test('should create a new user', async ({ request }) => {
const userVariables = {
data: {
firstName: 'Bob',
lastName: 'Bobson',
}
};
const operationName = 'CreateUser'
const { response, body } = await sendGraphQLRequest(request, GET_USERS, userVariables, operationName);
// Assert response code
expect(response.status()).toBe(200);
// Assert data content
expect(body.data.users.length).toBeGreaterThan(0);
expect(body.data.users[0]).toHaveProperty('email');
expect(body.data.users[0]).toHaveProperty('id');
expect(body.data.users[0].lastName).toBe('Bobson');
expect(body.data.users[0].firstName).toBe('Bob');
});
In this code, we:
Import all our bits
Set up our user variables
We also define the operation name, but you could wrap a specific query around the sendGraphQLRequest function and hand it off rather than have it in the test.
Sent off our request
Checked the results
Of course, you could also add a call to the database to ensure that the user persists, or this could be a precursor to a UI test; it creates the data more quickly.
Either way, hopefully this quick intro to querying GraphQL with Playwright will be helpful and alleviate at least some pain!
Comentarios