Test your Prisma app Part 2: prisma-fabbrica, test data factory utility

Yosuke Kurami
3 min readJan 4, 2023

--

Photo by Michael Dziedzic from unsplash

In the previous post, I introduced how to test Prisma app with isolated transaction via jest-prisma. In this post, I introduce another package, prisma-fabbrica, which helps to create test data.

Motivation

We need to set up test data sufficient for each test case. Especially with integrated testing, in other words using a real database, we should insert test data as precondition.

For example, imagine the following Prisma schema, which represents that a User model has many posts and a Post model belongs to a user.

model User {
id String @id
email String @unique
name String
posts Post[]
}

model Post {
id String @id
title String
author User @relation(fields: [authorId], references: [id])
authorId String
}

And if you want to test some logic to update title of a post such as:

/* src/updatePostTitle.ts */
export async function updatePostTitle(postId: string, title: string) {
return await prisma.post.update({
where: {
id: postId
},
data: {
title
}
});
}

Test code for the above function is the following:

/* src/updatePostTitle.test.ts */
import { updatePostTitle } from "./updatePostTitle";

test("updatePostTitle", async () => {
await prisma.post.create({
data: {
id: "post",
title: "title",
author: {
create: {
data: {
id: "user001",
name: "Quramy",
email: "Quramy@myservice.com"
}
}
}
}
});

await expect(updatePostTitle("post", "new title")).resolve.toMatchObject({
title: "new title"
});
});

Okay, the above test works and passes. But take careful at the code. It contains some useless information. The post.create block contains a user model fields although the test case is not interested in post's author.

await prisma.post.create({
data: {
id: "post",
title: "title",
author: {
create: {
data: {
id: "user001",
name: "Quramy",
email: "Quramy@myservice.com"
}
}
}
}
});

The above code lines increases as the number of required fields in User model increases.

I don’t like to set up unneeded models (e.g. author in the above case) and I want to keep tests to contain necessary and sufficient information.

Test data factory

I built prisma-fabbrica inspired from factory_bot, a utility library to set up Active Record models in Ruby.

The above test code for updatePostTitle can be rewritten as the following using prisma-fabbrica:

/* src/updatePostTitle.test.ts */
import { updatePostTitle } from "./updatePostTitle";
import { PostFactory } from "./factories";

test("updatePostTitle", async () => {
const post = await PostFactory.create({ title: "title" });
await expect(updatePostTitle(post.id, "new title")).resolve.toMatchObject({
title: "new title"
});
});
/* src/factories.ts */
import { defineUserFactory, definePostFactory } from "./__generated__/fabbrica";

export const UserFactory = defineUserFactory();

export const PostFactory = definePostFactory({
defaultData: {
author: UserFactory
}
});

prisma-fabbrica is a Prisma generator and generates functions to define model factory(e.g. defineUserFactory and definePostFactory).

Model factories work as “shared fixture”. So you can re-use model factories in multiple test cases once you define them.

Features of prisma-fabbrica

Auto complete required scalar fields

The User model has some required scalar fields, id, email and name

model User {
id String @id
email String @unique
name String
posts Post[]
}

But you don’t need give the required values. Factory automatically fills values corresponding their types.

await UserFactory.create();
// or
await UserFactory.create({ email: "test@example.com" });
// or
await UserFactory.create({ id: "user001", email: "test@example.com" });

Relations

You can order to create relations in various ways.

const PostFactory = definePostFactory({
defaultData: { author: UserFactory }
});

await PostFactory.create(); // Create not only post but also user
// Create a user that has 2 posts
await UserFactory({
posts: {
create: await PostFactory.buildList(2)
}
});
const PostFactory = definePostFactory(async () => ({
defaultData: {
author: connectOrCreate({
where: { id: "user" },
create: await UserFactory.build({
id: "user"
})
})
}
}));

// The following posts belongs to the same user
await PostFactory.create();
await PostFactory.create();

See document if you want more details.

Summary

In this post, I introduced how to write Primsa integrated test codes efficiently via prisma-fabbrica.

Finally, I put an example repository using jest-prisma, explained in my previous post, and prisma-fabbrica.

I hope this helps you.

--

--

Yosuke Kurami

Front-end web developer. TypeScript, Angular and Vim, weapon of choice.