Episode 21 membangun strategi testing untuk GraphQL: test pyramid, unit testing resolver dengan mocking dependencies, integration testing dengan test server dan supertest, schema testing dan breaking change detection, mocking data dengan GraphQL Tools, hingga E2E testing dengan Cypress.

API GraphQL yang kompleks tanpa testing adalah bom waktu. Episode 21 membangun strategi testing yang menyeluruh: dari unit test resolver terkecil sampai E2E test alur pengguna penuh.
Kita akan mempelajari test pyramid khusus GraphQL, menulis unit test dengan mocking, integration test dengan server sungguhan, schema testing untuk mendeteksi breaking change, mocking data, hingga E2E dengan Cypress.
Piramida testing berlaku untuk GraphQL dengan lapisan spesifik:
Install dependency testing dengan npm install -D jest ts-jest @types/jest supertest @apollo/server.
Prinsipnya: banyak unit test, cukup integration test, sedikit E2E. E2E paling lambat dan paling rapuh, jadi hanya untuk alur kritis.
Unit test memanggil resolver langsung dengan parent, args, dan context buatan:
import { resolvers } from "./resolvers";
test("post resolver mengambil post dari context", async () => {
const mockDb = {
posts: { find: jest.fn().mockResolvedValue({ id: 1, title: "Halo" }) },
};
const result = await resolvers.Query.post(null, { id: "1" }, { db: mockDb });
expect(mockDb.posts.find).toHaveBeenCalledWith("1");
expect(result).toEqual({ id: 1, title: "Halo" });
});Perhatikan: context dimock penuh, sehingga resolver diuji dalam isolasi. Ini membuat kegagalan mudah dilacak — masalahnya pasti di logika resolver, bukan di database.
Untuk resolver terproteksi, uji semua jalur: tanpa user, user salah role, dan user berhak:
test("deletePost menolak user tanpa izin", async () => {
const args = { id: "1" };
const ctx = { user: { id: 2, role: "MEMBER" } };
await expect(
resolvers.Mutation.deletePost(null, args, ctx)
).rejects.toThrow("tidak berhak");
});Integration test menjalankan server Apollo dalam memori lalu mengirim request seperti client:
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 0 } });
test("query posts mengembalikan data", async () => {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "{ posts { id title } }" }),
});
const json = await res.json();
expect(json.errors).toBeUndefined();
expect(json.data.posts).toBeDefined();
});Pakai port: 0 agar server memakai port acak dan tidak bentrok antar test. Untuk database, gunakan database in-memory (misalnya better-sqlite3 dengan Prisma) agar test cepat dan hermetis.
Jika memakai Express, supertest menjadi pilihan natural untuk menguji endpoint:
import request from "supertest";
import app from "./app";
test("mutation login mengembalikan token", async () => {
const res = await request(app)
.post("/graphql")
.send({ query: `mutation { login(email: "a@b.c", password: "rahasia") { token } }` });
expect(res.body.data.login.token).toBeDefined();
});Schema adalah contract, jadi uji perubahan schema secara otomatis:
npx @graphql-inspector/cli diff schema.graphql new-schema.graphqlGraphQL Inspector membandingkan dua schema dan melaporkan breaking change: field dihapus, tipe diubah, non-null ditambahkan. Jalankan ini di CI (episode 32) untuk memblokir perubahan yang merusak client.
Untuk development frontend paralel, mock seluruh resolver dari schema:
import { addMocksToSchema } from "@graphql-tools/mock";
import { makeExecutableSchema } from "@graphql-tools/schema";
const schema = makeExecutableSchema({ typeDefs });
const mockSchema = addMocksToSchema({ schema });Gabungkan dengan Faker.js untuk data realistis. Pola ini membuat frontend bisa dikembangkan dan diuji sebelum backend selesai — kita bahas lagi di episode 46.
E2E test memvalidasi alur nyata pengguna di browser:
npm install -D cypressdescribe("Login flow", () => {
it("login dan melihat dashboard", () => {
cy.visit("/login");
cy.get("[data-testid=email]").type("a@b.c");
cy.get("[data-testid=password]").type("rahasia");
cy.get("[data-testid=submit]").click();
cy.contains("Selamat datang").should("be.visible");
});
});Untuk mengontrol network GraphQL di Cypress, gunakan cy.intercept untuk memotong query tertentu dan mengembalikan fixture — membuat E2E test stabil tanpa bergantung server.
Inti yang harus dibawa pulang:
cy.intercept menstabilkan E2E test.Di episode 22 selanjutnya kalian akan mempelajari Apollo Federation untuk microservices — konsep supergraph dan gateway, fitur Federation 2 seperti @shareable dan @override, membangun subgraph dengan @key dan @extends, setup Apollo Gateway, hingga strategi migrasi dari monolith. Satu schema, banyak service!