cloudflare / workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
https://developers.cloudflare.com/workers/
Apache License 2.0
2.41k stars 593 forks source link

🚀 Feature Request: Durable Object mocks #5644

Open punkeel opened 2 months ago

punkeel commented 2 months ago

Describe the solution

I'm trying to test a Worker script talking to a Durable Object, using Workers RPC and Vitest.

This (will) work fine once #5508 lands, with one caveat: I don't think it's possible to mock the Durable Object functions–they're hidden behind a get(id) call.

Can we simplify how mocking works in this scenario? (This is not the right place to argue on mocking, but real quick: I'm trying to test error handling and other tricky unhappy paths)

This may be related to #5464.

mrbbot commented 2 months ago

Hey! đź‘‹ This should be possible with regular Vitest mocking. Either mock the functions on the Durable Object's prototype, or use the runInDurableObject() helper method to mock a specific instance. I haven't tested this code, but something like this should work...

import { vi } from "vitest";
import { runInDurableObject, env } from "cloudflare:test";
import { MyObject } from "../src/";

afterEach(() => {
  vi.restoreAllMocks();
});

it("mock all Durable Objects", async () => {
  // `function () {}` over `() => {}` to avoid capturing `this`
  vi.spyOn(MyObject.prototype, "instanceMethod").mockImplementation(function () {
    ...
  });

  const stub = env.MY_OBJECT.get(...);
  const response = await stub.fetch(...);
});

it("mock specific Durable Object", async () => {
  const stub = env.MY_OBJECT.get(...);

  await runInDurableObject(stub, async (instance: MyObject) => {
    vi.spyOn(instance, "instanceMethod").mockImplementation(() => {
      // ...
    });
  });

  const response = await stub.fetch(...);
});
threepointone commented 2 weeks ago

Does this fix your problem @punkeel?