Part 7: Testing, Performance, and Deployment
Shipping GraphQL with Confidence
Testing the TypeScript Subgraph
Unit Testing Resolvers with executeOperation
executeOperationnpm install --save-dev jest @types/jest ts-jest// jest.config.ts
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
};// src/__tests__/products.test.ts
import { ApolloServer } from '@apollo/server';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { typeDefs } from '../schema';
import { resolvers } from '../resolvers';
import { AppContext } from '../context';
// ─── Minimal fake context ───────────────────────────────────────────────────
function buildTestContext(overrides: Partial<AppContext> = {}): AppContext {
const prismaMock = {
product: {
findUnique: jest.fn(),
findMany: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
count: jest.fn(),
},
category: {
findUnique: jest.fn(),
findMany: jest.fn(),
},
};
return {
prisma: prismaMock as any,
loaders: {
category: { load: jest.fn() } as any,
productVariants: { load: jest.fn() } as any,
},
user: { id: 'user-1', email: '[email protected]', role: 'ADMIN' },
...overrides,
};
}
// ─── Test Suite ─────────────────────────────────────────────────────────────
describe('Product resolvers', () => {
let server: ApolloServer<AppContext>;
beforeAll(async () => {
server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
await server.start();
});
afterAll(async () => {
await server.stop();
});
it('returns a product by id', async () => {
const productFixture = {
id: 'prod-1',
name: 'Wireless Keyboard',
slug: 'wireless-keyboard',
price: 79.99,
currency: 'USD',
stockCount: 5,
categoryId: 'cat-1',
tags: ['electronics'],
status: 'PUBLISHED',
description: null,
createdAt: new Date(),
updatedAt: new Date(),
};
const ctx = buildTestContext();
(ctx.prisma.product.findUnique as jest.Mock).mockResolvedValue(productFixture);
(ctx.loaders.category.load as jest.Mock).mockResolvedValue({
id: 'cat-1',
name: 'Electronics',
slug: 'electronics',
});
const response = await server.executeOperation(
{
query: `
query GetProduct($id: UUID!) {
product(id: $id) {
id
name
price
inStock
category { name }
}
}
`,
variables: { id: 'prod-1' },
},
{ contextValue: ctx },
);
expect(response.body.kind).toBe('single');
const data = (response.body as any).singleResult.data;
expect(data.product.id).toBe('prod-1');
expect(data.product.name).toBe('Wireless Keyboard');
expect(data.product.inStock).toBe(true); // stockCount > 0
expect(data.product.category.name).toBe('Electronics');
expect(ctx.prisma.product.findUnique).toHaveBeenCalledWith({ where: { id: 'prod-1' } });
});
it('returns null for a non-existent product', async () => {
const ctx = buildTestContext();
(ctx.prisma.product.findUnique as jest.Mock).mockResolvedValue(null);
const response = await server.executeOperation(
{ query: `query { product(id: "does-not-exist") { id name } }` },
{ contextValue: ctx },
);
const data = (response.body as any).singleResult.data;
expect(data.product).toBeNull();
});
it('rejects createProduct without an editor role', async () => {
const ctx = buildTestContext({
user: { id: 'user-2', email: '[email protected]', role: 'VIEWER' },
});
const response = await server.executeOperation(
{
query: `
mutation {
createProduct(input: {
name: "Test Product"
slug: "test-product"
price: 9.99
currency: "USD"
categoryId: "cat-1"
}) {
id
}
}
`,
},
{ contextValue: ctx },
);
const errors = (response.body as any).singleResult.errors;
expect(errors).toBeDefined();
expect(errors[0].extensions.code).toBe('FORBIDDEN');
});
});Integration Testing with Testcontainers
Testing the Python Subgraph
DataLoader Performance Verification
Query Complexity Limiting
Persisted Queries
Deployment
Health Checks
Environment Variables
Variable
Service
Purpose
Recommended Production Checklist
CI Supergraph Composition
Series Summary
Part
What Was Covered
Last updated