---
title: 6. Testing with Prisma
---

## Chapter 6: Testing With Prisma

There's a couple of things you'll have to do in order to run integration tests against your API now that it's connected to a real development database. In this chapter, you'll learn about:

- Custom Jest environment
- Integration test with a real database

## How does it work?

To perform integration testing against a real database, here are the high level steps we will follow _for every test_:

- Connect to a SQLite database. Most likely your dev database.
- Migrate our database schema to a randomly generated schema of that database. This ensures that every tests runs from a clean un-seeded database
- Make the Prisma Client connect to that SQLite database
- Run your test
- Teardown the schema entirely

## Setting up the environment

To achieve some of the steps described above, we'll tweak our test context.

First, install the `sqlite3` and `nanoid` packages

```bash copy
npm install --save-dev sqlite3 @types/sqlite3
```

Then, head to your `tests/__helpers.ts` file to add the following imports and code

<TabbedContent tabs={['Diff', 'Code']}>
<tab>

```ts
// tests/__helpers.ts
+import { PrismaClient } from "@prisma/client";
import { ServerInfo } from "apollo-server";
+import { execSync } from "child_process";
import getPort, { makeRange } from "get-port";
import { GraphQLClient } from "graphql-request";
+import { join } from "path";
+import { Database } from "sqlite3";
+import { db } from "../api/db";
import { server } from "../api/server";

type TestContext = {
  client: GraphQLClient;
+  db: PrismaClient;
};

export function createTestContext(): TestContext {
  let ctx = {} as TestContext;
  const graphqlCtx = graphqlTestContext();
+  const prismaCtx = prismaTestContext();

  beforeEach(async () => {
    const client = await graphqlCtx.before();
+    const db = await prismaCtx.before();

    Object.assign(ctx, {
      client,
+      db,
    });
  });

  afterEach(async () => {
    await graphqlCtx.after();
+    await prismaCtx.after();
  });

  return ctx;
}

function graphqlTestContext() {
  let serverInstance: ServerInfo | null = null;

  return {
    async before() {
      const port = await getPort({ port: makeRange(4000, 6000) });

      serverInstance = await server.listen({ port });
+     // Close the Prisma Client connection when the Apollo Server is closed
+     serverInstance.server.on("close", async () => {
+       db.$disconnect() 
+     });

      return new GraphQLClient(`http://localhost:${port}`);
    },
    async after() {
      serverInstance?.server.close();
    },
  };
}

+function prismaTestContext() {
+  const prismaBinary = join(__dirname, "..", "node_modules", ".bin", "prisma");
+  let prismaClient: null | PrismaClient = null;
+
+  return {
+     async before() {
+
+       // Run the migrations to ensure our schema has the required structure
+       execSync(`${prismaBinary} db push --preview-feature`,);
+
+       // Construct a new Prisma Client connected to the generated schema
+       prismaClient = new PrismaClient();
+
+       return prismaClient;
+     },
+     async after() {
+       // Drop the schema after the tests have completed
+       const client = new Database(':memory:');

+       await client.close();
+
+       // Release the Prisma Client connection
+       await prismaClient?.$disconnect();
+     },
+   };
+ }
```

</tab>
<tab>

```ts copy
// tests/__helpers.ts
import { PrismaClient } from "@prisma/client";
import { ServerInfo } from "apollo-server";
import { execSync } from "child_process";
import getPort, { makeRange } from "get-port";
import { GraphQLClient } from "graphql-request";
import { join } from "path";
import { Database } from "sqlite3";
import { db } from "../api/db";
import { server } from "../api/server";

type TestContext = {
  client: GraphQLClient;
  db: PrismaClient;
};

export function createTestContext(): TestContext {
  let ctx = {} as TestContext;
  const graphqlCtx = graphqlTestContext();
  const prismaCtx = prismaTestContext();

  beforeEach(async () => {
    const client = await graphqlCtx.before();
    const db = await prismaCtx.before();

    Object.assign(ctx, {
      client,
      db,
    });
  });

  afterEach(async () => {
    await graphqlCtx.after();
    await prismaCtx.after();
  });

  return ctx;
}

function graphqlTestContext() {
  let serverInstance: ServerInfo | null = null;

  return {
    async before() {
      const port = await getPort({ port: makeRange(4000, 6000) });
      
      serverInstance = await server.listen({ port });
      // Close the Prisma Client connection when the Apollo Server is closed
      serverInstance.server.on("close", async () => {
        db.$disconnect() 
      });

      return new GraphQLClient(`http://localhost:${port}`);
    },
    async after() {
      serverInstance?.server.close();
    },
  };
}

function prismaTestContext() {
  const prismaBinary = join(__dirname, "..", "node_modules", ".bin", "prisma");
  let prismaClient: null | PrismaClient = null;

  return {
    async before() {
      // Run the migrations to ensure our schema has the required structure
      execSync(`${prismaBinary} db push --preview-feature`,);

      // Construct a new Prisma Client connected to the generated schema
      prismaClient = new PrismaClient();

      return prismaClient;
    },
    async after() {
      // Drop the schema after the tests have completed
      const client = new Database(':memory:');

      await client.close();

      // Release the Prisma Client connection
      await prismaClient?.$disconnect();
    },
  };
}
```

</tab>
</TabbedContent>

The `prismaTestContext` is in charge of a couple of things:

1. Connect to an in-memory instance of the SQLite database
2. Pushes the Prisma Schema to the database
3. Generates a new Prisma Client
4. Add an instance of a Prisma Client connected to the schema specifically for the test

## Updating our test

We're ready to update our test so that it uses our database. Wait though. Is there even something to change?
No, absolutely nothing. In fact, you can already try running Jest again and your test should pass. That's precisely the point of integration tests.

There's one thing we can do though. If you remember our previous test, the only part we couldn't test was whether or not the data had properly been persisted into the database.

Let's use the `ctx.db` property to fetch our database right after we've published the draft to ensure that it's been created by snapshotting the result.

```ts
// tests/Post.test.ts

it('ensures that a draft can be created and published', async () => {
  // ...

  // Publish the previously created draft
  const publishResult = await ctx.client.request(
    `
    mutation publishDraft($draftId: Int!) {
      publish(draftId: $draftId) {
        id
        title
        body
        published
      }
    }
  `,
    { draftId: draftResult.createDraft.id }
  )

  // Snapshot the published draft and expect `published` to be true
  expect(publishResult).toMatchInlineSnapshot(`
    Object {
      "publish": Object {
        "body": "...",
        "id": 1,
        "published": true,
        "title": "Nexus",
      },
    }
  `)

+ const persistedData = await ctx.db.post.findMany()

+ expect(persistedData).toMatchInlineSnapshot()
})
```

Now run your tests

```bash-symbol
npm run test
```

The new snapshot should look like the following. It proves that our database did persist that data and that we have exactly one item in it.

```ts
expect(persistedData).toMatchInlineSnapshot(`
+   Array [
+     Object {
+       "body": "...",
+       "id": 1,
+       "published": true,
+       "title": "Nexus",
+     },
+   ]
  `)
```

## Wrapping up

Congrats, you've performed your first real-world integration test. The fact that integration tests are completely decoupled from the implementation of your GraphQL API makes it a lot easier to maintain your test suite as you evolve your API. What matters is only the data that it produces, which also helps you cover your app a lot more than a single unit test.

This is the end of the tutorial. Thanks for trying it out and please share any feedback you have with us!
