Skip to content

Connectクライアントをテストする(フロントエンド)

msw を使ってテストする。msw 自体はMSW(Mock Service Worker)とはで書いた。

Terminal window
npm install --save-dev msw

ここではブラウザを使わないので、npx msw init public は必要ない。また、MSWで型安全にConnectの通信をモックするのように型安全にできるといいのだが、この記事はv1時代のもので、v2ではうまくいかなかった。

フロントエンドのディレクトリ構造ベストプラクティスに従って、以下のように配置する。

features/
wallet/
api/
index.test.ts
index.ts
testing/
mocks/
handlers.ts
server.ts

まずはハンドラから書く。

import { http, HttpResponse } from "msw";
import { WalletService } from "../../../../../proto/gen/wallet/v1/wallet_pb";
export const handlers = [
http.all(
`http://localhost:8080/${WalletService.typeName}/CreateMerchant`,
() => {
return HttpResponse.json({ id: "999" }, { status: 200 });
},
),
]

ハンドラを使ったサーバを書く。

import { setupServer } from "msw/node";
import { handlers } from "./handlers.js";
export const server = setupServer(...handlers);

クライアントの実装は単純に createClient するだけ。ここで型の検査と変換をしてもいいが、やってない。

import type { Client } from "@connectrpc/connect";
import { createClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { WalletService } from "../../../../proto/gen/wallet/v1/wallet_pb.js";
export function createWalletClient(
baseUrl: string,
): Client<typeof WalletService> {
const transport = createConnectTransport({ baseUrl });
return createClient(WalletService, transport);
}

テストは素朴に書く。エラーはErrors | Connectに書かれている通りに返せばいい。

describe("WalletClinet", () => {
describe("createMerchant", () => {
it("should return a new id", async () => {
const { id } = await client.createMerchant({
name: "New Shop",
categoryId: "345",
});
expect(id).toBe("999");
});
it("should raise an error when something happen", async () => {
server.use(
http.all(
`http://localhost:8080/${WalletService.typeName}/CreateMerchant`,
() => {
return HttpResponse.json(
{
code: "invalid_argument",
message: "aaa",
},
{ status: 400 },
);
},
),
);
let error: ConnectError | undefined = undefined;
try {
await client.createMerchant({ ... });
} catch (e) {
if (e instanceof ConnectError) {
error = e;
}
}
expect(error?.code).toBe(Code.InvalidArgument);
expect(error?.rawMessage).toBe("aaa");
});
});
});