Back to skills
SKILL.md
Apollo Server Tests
ASecurityWraps Apollo Server testing patterns: `server.executeOperation()` (in-process, no HTTP), `supertest` against an ephemeral-port HTTP server (port 0), context injection via the `contextValue` second-argument, and assertion patterns for response shape + errors. Includes the production-config gates testable through this skill - introspection-disabled, persisted-query mode, hideSchemaDetailsFromClientErrors. Use when writing tests for an Apollo Server v4+ GraphQL service.
- 10 stars
- 0 votes
- 0 copies
- 1 view
- Added October 12, 2026
Works with
Security analysis
96/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 12 files and shows the line behind each finding
npx -y skills add testland/qa --skill apollo-server-tests --agent claude-codeAre you the author of Apollo Server Tests?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/testland-apollo-server-tests)---
name: apollo-server-tests
description: "Wraps Apollo Server testing patterns: `server.executeOperation()` (in-process, no HTTP), `supertest` against an ephemeral-port HTTP server (port 0), context injection via the `contextValue` second-argument, and assertion patterns for response shape + errors. Includes the production-config gates testable through this skill - introspection-disabled, persisted-query mode, hideSchemaDetailsFromClientErrors. Use when writing tests for an Apollo Server v4+ GraphQL service."
---
# apollo-server-tests
## Overview
`executeOperation` initializes automatically - no startup needed for
unit-style tests against the schema in-process. For HTTP-layer tests
(CORS, middleware, response headers), use `supertest` against an
ephemeral-port server.
## When to use
- Unit tests for resolvers using `executeOperation`.
- Integration tests for HTTP-layer behaviour using `supertest`.
- Production-config gates: assert introspection / APQ /
hideSchemaDetails settings match your production config.
## Authoring
### Install
```bash
npm install --save-dev @apollo/server supertest @types/supertest
```
### In-process tests with `executeOperation`
```typescript
import { ApolloServer } from '@apollo/server';
import { typeDefs, resolvers } from './schema';
const testServer = new ApolloServer({ typeDefs, resolvers });
test('returns greeting', async () => {
const response = await testServer.executeOperation({
query: 'query SayHelloWorld($name: String) { hello(name: $name) }',
variables: { name: 'world' },
});
// Parse/validation/execution errors surface in `errors`, not thrown
if (response.body.kind !== 'single') throw new Error('expected single');
expect(response.body.singleResult.errors).toBeUndefined();
expect(response.body.singleResult.data?.hello).toBe('Hello world!');
});
```
### Context injection (auth, datasources)
```typescript
const res = await testServer.executeOperation(
{ query: GET_LAUNCH, variables: { id: 1 } },
{
contextValue: {
user: { id: 1, email: 'a@a.a' },
dataSources: { userAPI, launchAPI },
},
},
);
```
`contextValue` bypasses the production context function (which parses headers).
### HTTP-layer tests with supertest
```typescript
import { startStandaloneServer } from '@apollo/server/standalone';
import request from 'supertest';
let server: ApolloServer;
let url: string;
beforeAll(async () => {
server = new ApolloServer({ typeDefs, resolvers });
({ url } = await startStandaloneServer(server, {
listen: { port: 0 }, // OS picks port → parallel-test safe
}));
});
afterAll(async () => {
await server?.stop();
});
it('says hello over HTTP', async () => {
const response = await request(url)
.post('/')
.send({ query: '{ hello }' });
expect(response.status).toBe(200);
expect(response.body.data?.hello).toBeDefined();
});
```
## Running
### Standard test commands
```bash
npm test # jest / vitest pick up *.test.ts
npx jest schema.test.ts -t "introspection"
```
### Production-config tests (most important)
Assert prod-time security defaults under `NODE_ENV=production`:
```typescript
import { ApolloServer } from '@apollo/server';
test('introspection disabled when production', async () => {
process.env.NODE_ENV = 'production';
const prodServer = new ApolloServer({
typeDefs, resolvers,
introspection: process.env.NODE_ENV !== 'production',
});
const resp = await prodServer.executeOperation({
query: '{ __schema { types { name } } }',
});
if (resp.body.kind !== 'single') throw new Error('expected single');
expect(resp.body.singleResult.errors).toBeDefined();
expect(resp.body.singleResult.errors?.[0].message).toMatch(/introspection/i);
});
test('hideSchemaDetailsFromClientErrors strips did-you-mean', async () => {
const server = new ApolloServer({
typeDefs, resolvers,
hideSchemaDetailsFromClientErrors: true,
});
const resp = await server.executeOperation({
query: '{ usre { id } }', // typo
});
if (resp.body.kind !== 'single') throw new Error('expected single');
expect(JSON.stringify(resp.body.singleResult.errors)).not.toMatch(/did you mean/i);
});
```
### Persisted-query mode test
Strict allowlist (APQ) mode:
```typescript
test('strict APQ rejects unregistered hash', async () => {
const server = new ApolloServer({
typeDefs, resolvers,
persistedQueries: false, // turn off auto-register
plugins: [/* strict-allowlist plugin from manifest */],
});
const { url } = await startStandaloneServer(server, { listen: { port: 0 } });
const resp = await request(url).post('/').send({
extensions: { persistedQuery: { version: 1, sha256Hash: 'deadbeef'.repeat(8) } },
});
expect(resp.body.errors[0].extensions.code).toMatch(/PERSISTED_QUERY/);
});
```
## Parsing results
The `executeOperation` response shape is discriminated:
```typescript
type ExecuteOperationResult =
| { body: { kind: 'single'; singleResult: { data?: ...; errors?: ... } } }
| { body: { kind: 'incremental'; ... } };
```
Always check `kind === 'single'` first. The errors array contains
`GraphQLError` objects with `message`, `path`, `extensions.code`
(e.g., `'BAD_USER_INPUT'`, `'UNAUTHENTICATED'`,
`'PERSISTED_QUERY_NOT_FOUND'`).
For supertest: standard HTTP response; `response.body.errors` if
GraphQL-level error, `response.status` if transport error.
## CI integration
```yaml
# .github/workflows/graphql-tests.yml
name: graphql
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
- name: Production-config assertions
env:
NODE_ENV: production
run: npx jest tests/production-config/ --forceExit
```
The production-config jobs run **separately** with `NODE_ENV=production`
so the actual prod-time defaults are exercised.
## Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Tests in `NODE_ENV=test` only | Production defaults differ; introspection-disabled gate untested | Separate prod-config test job |
| `executeOperation` for HTTP-layer concerns | Skips middleware, CORS, headers | Use supertest for HTTP-layer |
| Hardcoded port `4000` in tests | Parallel CI conflicts | `port: 0` |
| Forgetting `server.stop()` in `afterAll` | Connection leak across tests | Always stop |
| Asserting `errors[0].message` string | Brittle to wording / i18n | Assert `extensions.code` |
| Skipping `contextValue` injection | Tests use real auth headers; flaky | Inject mocked context per test |
| One mega-test for the whole schema | Failures hard to diagnose | One test per resolver / operation |
| `data` access without checking `errors` | Errors masked; false positives | Check `errors === undefined` first |
## Limitations
- **`executeOperation` skips HTTP.** Auth via headers, CORS,
rate-limiting middleware are not exercised. Use supertest for those.
- **Doesn't test subscriptions over WS.** Subscriptions need a
WebSocket server; use `graphql-ws` test patterns.
- **Doesn't catch type-level bugs at runtime.** Test the runtime
behaviour, not the type definitions.
- **Doesn't replace contract tests.** Mocked context can drift from
prod; pair with schema-regression contract tests.
## References
- Apollo Server testing:
[apollographql.com/docs/apollo-server/testing/testing](https://www.apollographql.com/docs/apollo-server/testing/testing)
- Apollo Server API - introspection, persisted queries, error config:
[apollographql.com/docs/apollo-server/api/apollo-server](https://www.apollographql.com/docs/apollo-server/api/apollo-server)
Files in this skill
- .tessl-plugin/plugin.json
- SKILL.md
- evals/launch-checklist-lands-on-the-suite/criteria.json
- evals/launch-checklist-lands-on-the-suite/task.md
- evals/one-test-called-the-api-works/criteria.json
- evals/one-test-called-the-api-works/task.md
- evals/preflight-blocked-after-the-web-app-moved-origin/criteria.json
- evals/preflight-blocked-after-the-web-app-moved-origin/task.md
- evals/security-questionnaire-on-settings-nobody-tested/criteria.json
- evals/security-questionnaire-on-settings-nobody-tested/task.md
- evals/three-hours-of-null-billing-and-a-green-job/criteria.json
- evals/three-hours-of-null-billing-and-a-green-job/task.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.