top of page

How to Structure Graphql Tests in Playwright

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


bottom of page