2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

テストを書くときにPrismaのインスタンスをモックする方法

Posted at

Remixでアプリケーションを開発していると、Prismaの恩恵をとても受けると思います。
ただ、いくらアーキテクチャに沿ってレイヤーを分割しても、テストを書いていると必ずPrismaに依存するコードに遭遇します。

RemixのIndie Stackなどのテンプレートを使って開発をする場合、db.server.tsにてPrismaのインスタンスを生成します。

db.server.ts
import { PrismaClient } from "@prisma/client";

let prisma: PrismaClient;
...
prisma = new PrismaClient();
...
export { prisma };

たとえば、このprismaを使ってデータを取得する場合、次のような実装になると思います。

UserServiceImpl.server.ts
export interface UserService {
  getUserById(id: string): Promise<User | null>;
}

export class UserServiceImpl implements UserService {
  async getUserById(id: string): Promise<User | null> {
    const user = await prisma.user.findUnique({
      where: { id },
      include: {
        preference: true,
      },
    });

    return user ?? null;
  }
}

この場合UserServiceImplprismaに依存しています。
そのため、このクラスに対してテストを書く場合、prismaをモックする必要があります。

モックをするために、db.server.tsが存在するディレクトリに__mocks__ディレクトリを作成し、db.server.tsをモックするファイルを作成します。

├── app
│   ├── __mocks__
│   │   └── db.server.ts
│   └── db.server.ts

そして、UserServiceImplでは、findUniqueメソッドを呼び出しているので、__mocks__/db.server.tsfindUniqueメソッドをモックします。

__mocks__/db.server.ts
import { faker } from "@faker-js/faker";

export const prisma = {
  user: {
    findUnique: vi.fn().mockImplementation((args) => {
      return {
        id: args.where.id,
        name: faker.name.findName(),
        email: faker.internet.email(),
        preference: {
          id: faker.datatype.uuid(),
          userId: args.where.id,
          theme: "light",
        },
      };
    }),
  }
}

それでは実際にテストを書いてみます。
実は、__mocks__/db.server.tsを読み込んでモックを適用するには、モック対象のモジュールをvi.mockで読み込む必要があります。

UserServiceImpl.server.test.ts
import { faker } from "@faker-js/faker";
import { UserServiceImpl } from "../UserServiceImpl.server";
import { prisma } from "../../../../../db.server";
// app/__mocks__/db.server.tsが読み込まれる
vi.mock("../../../../../db.server");

describe("UserServiceImpl", () => {
  test("should return user when user exists", async () => {
    // Arrange
    const userId = faker.datatype.uuid();
    const userService = new UserServiceImpl();
    // Act
    const user = await userService.getUserById(userId);
    // Assert
    expect(user?.id).toBe(userId);
    expect(user?.preference).not.toBeNull();
  });
});

これでUserServiceImplのテストが書けました。
次に、上記の内容を維持したままfindUnique()メソッドがnullを返す場合をテストしてみます。
それを実現するために、prisma.user.findUniqueに対してmockResolvedValueOnceを使ってnullを返すようにします。

UserServiceImpl.server.test.ts
...
test("should return null when user does not exist", async () => {
  // Arrange
  const findUnique = prisma.user.findUnique as jest.Mock<any, any>;
  findUnique.mockResolvedValueOnce(null);
  const userId = faker.datatype.uuid();
  const userService = new UserServiceImpl();
  // Act
  const user = await userService.getUserById(userId);
  // Assert
  expect(user).toBeNull();
});

これで、prismaに依存したUserServiceImplのテストが書けました。
今回の記事ではRemixの慣例的にVitestを使いましたが、Jestを使っても同じようにテストを書くことができますので是非お試しください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?