# Testing > Source: `src/content/docs/actors/testing.mdx` > Canonical URL: https://rivet.dev/docs/actors/testing > Description: Rivet provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. --- ## Setup To set up testing with Rivet: ```bash # Install Vitest npm install -D vitest # Run tests npm test ``` ## Basic Testing Setup Rivet includes a test helper called `setupTest` that starts your registry in test mode and returns a client connected to it. This allows for fast, isolated tests without external dependencies. ```ts import { test, expect } from "vitest"; import { setupTest } from "rivetkit/test"; import { actor, setup } from "rivetkit"; // Define the actor const myActor = actor({ state: { value: "initial" }, actions: { someAction: (c) => { c.state.value = "updated"; return c.state.value; }, getState: (c) => { return c.state.value; } } }); // Create the registry const registry = setup({ use: { myActor } }); // Test the actor test("my actor test", async (testCtx) => { const { client } = await setupTest(testCtx, registry); // Now you can interact with your actor through the client const myActorHandle = client.myActor.getOrCreate(["test"]); // Test your actor's functionality await myActorHandle.someAction(); // Make assertions const result = await myActorHandle.getState(); expect(result).toEqual("updated"); }); ``` ## Testing Actor State State persists within each test, allowing you to verify that your actor correctly maintains state between operations. ```ts import { test, expect } from "vitest"; import { setupTest } from "rivetkit/test"; import { actor, setup } from "rivetkit"; // Define the counter actor const counter = actor({ state: { count: 0 }, actions: { increment: (c) => { c.state.count += 1; c.broadcast("newCount", c.state.count); return c.state.count; }, getCount: (c) => { return c.state.count; } } }); // Create the registry const registry = setup({ use: { counter } }); // Test state persistence test("actor should persist state", async (testCtx) => { const { client } = await setupTest(testCtx, registry); const counterHandle = client.counter.getOrCreate(["test"]); // Initial state expect(await counterHandle.getCount()).toBe(0); // Modify state await counterHandle.increment(); // Verify state was updated expect(await counterHandle.getCount()).toBe(1); }); ``` ## Testing Events For actors that emit events, you can verify events are correctly triggered by subscribing to them: ```ts import { test, expect, vi } from "vitest"; import { setupTest } from "rivetkit/test"; import { actor, setup } from "rivetkit"; interface ChatMessage { username: string; message: string; } // Define the chat room actor const chatRoom = actor({ state: { messages: [] as ChatMessage[] }, actions: { sendMessage: (c, username: string, message: string) => { c.state.messages.push({ username, message }); c.broadcast("newMessage", username, message); }, getHistory: (c) => { return c.state.messages; }, }, }); // Create the registry const registry = setup({ use: { chatRoom } }); // Test event emission test("actor should emit events", async (testCtx) => { const { client } = await setupTest(testCtx, registry); const chatRoomHandle = client.chatRoom.getOrCreate(["test"]); // Set up event handler with a mock function const mockHandler = vi.fn(); const conn = chatRoomHandle.connect(); conn.on("newMessage", mockHandler); // Trigger the event await conn.sendMessage("testUser", "Hello world"); // Wait for the event to be emitted await vi.waitFor(() => { expect(mockHandler).toHaveBeenCalledWith("testUser", "Hello world"); }); }); ``` ## Testing Schedules Rivet's schedule functionality can be tested by scheduling work and waiting for it to run: ```ts import { test, expect } from "vitest"; import { setupTest } from "rivetkit/test"; import { actor, setup } from "rivetkit"; // Helper to wait for a delay const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); // Define the scheduler actor const scheduler = actor({ state: { tasks: [] as string[], completedTasks: [] as string[] }, actions: { scheduleTask: (c, taskName: string, delayMs: number) => { c.state.tasks.push(taskName); // Schedule "completeTask" to run after the specified delay c.schedule.after(delayMs, "completeTask", taskName); return { success: true }; }, completeTask: (c, taskName: string) => { // This action will be called by the scheduler when the time comes c.state.completedTasks.push(taskName); return { completed: taskName }; }, getCompletedTasks: (c) => { return c.state.completedTasks; } } }); // Create the registry const registry = setup({ use: { scheduler } }); // Test scheduled tasks test("scheduled tasks should execute", async (testCtx) => { const { client } = await setupTest(testCtx, registry); const schedulerHandle = client.scheduler.getOrCreate(["test"]); // Set up a scheduled task await schedulerHandle.scheduleTask("reminder", 100); // 100ms in the future // Wait for the scheduled task to run await wait(150); // Verify the scheduled task executed expect(await schedulerHandle.getCompletedTasks()).toContain("reminder"); }); ``` Use a short real-time delay to wait for scheduled work to run. `setupTest` does not install fake timers, so if you want to use `vi.useFakeTimers()` you must enable it yourself and confirm it works with your selected runtime. ## Best Practices 1. **Isolate tests**: Each test should run independently, avoiding shared state. 2. **Test edge cases**: Verify how your actor handles invalid inputs, concurrent operations, and error conditions. 3. **Test scheduled operations**: Use short real-time delays to wait for scheduled work to run. 4. **Use realistic data**: Test with data that resembles production scenarios. `setupTest` starts the registry and disposes the returned client when the test finishes, so you can focus on writing effective tests for your business logic. ## API Reference - [`setupTest`](/typedoc/functions/rivetkit.test_mod.setupTest.html) - Test setup helper function _Source doc path: /docs/actors/testing_