Remixでアプリケーションを開発していると、Prismaの恩恵をとても受けると思います。
ただ、いくらアーキテクチャに沿ってレイヤーを分割しても、テストを書いていると必ずPrismaに依存するコードに遭遇します。
RemixのIndie Stackなどのテンプレートを使って開発をする場合、db.server.tsにてPrismaのインスタンスを生成します。
import { PrismaClient } from "@prisma/client";
let prisma: PrismaClient;
...
prisma = new PrismaClient();
...
export { prisma };
たとえば、このprisma
を使ってデータを取得する場合、次のような実装になると思います。
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;
}
}
この場合UserServiceImpl
はprisma
に依存しています。
そのため、このクラスに対してテストを書く場合、prisma
をモックする必要があります。
モックをするために、db.server.tsが存在するディレクトリに__mocks__
ディレクトリを作成し、db.server.ts
をモックするファイルを作成します。
├── app
│ ├── __mocks__
│ │ └── db.server.ts
│ └── db.server.ts
そして、UserServiceImpl
では、findUnique
メソッドを呼び出しているので、__mocks__/db.server.ts
でfindUnique
メソッドをモックします。
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
で読み込む必要があります。
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
を返すようにします。
...
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を使っても同じようにテストを書くことができますので是非お試しください。