|
Download __tests__/MSW.md from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/MSW.md
- Command line
-
hf download hf://SaylorTwift/openhands/__tests__/MSW.md
-
curl -L -o MSW.md https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/MSW.md
4.48 kB
| # Mock Service Worker (MSW) Guide | |
| ## Overview | |
| [Mock Service Worker (MSW)](https://mswjs.io/) is an API mocking library that intercepts outgoing network requests at the network level. Unlike traditional mocking that patches `fetch` or `axios`, MSW uses a Service Worker in the browser and direct request interception in Node.js—making mocks transparent to your application code. | |
| We use MSW in this project for: | |
| - **Testing**: Write reliable unit and integration tests without real network calls | |
| - **Development**: Run the frontend with mocked APIs when the backend isn't available or when working on features with pending backend APIs | |
| The same mock handlers work in both environments, so you write them once and reuse everywhere. | |
| ## Relevant Files | |
| - `src/mocks/handlers.ts` - Main handler registry that combines all domain handlers | |
| - `src/mocks/*-handlers.ts` - Domain-specific handlers (auth, conversation, etc.) | |
| - `src/mocks/browser.ts` - Browser setup for development mode | |
| - `src/mocks/node.ts` - Node.js setup for tests | |
| - `vitest.setup.ts` - Global test setup with MSW lifecycle hooks | |
| ## Development Workflow | |
| ### Running with Mocked APIs | |
| ```sh | |
| # Run with API mocking enabled | |
| npm run dev:mock | |
| ``` | |
| This command sets `VITE_MOCK_API=true` which activates the MSW Service Worker to intercept requests. | |
| ## Writing Tests | |
| ### Service Layer Mocking (Recommended) | |
| For most tests, mock at the service layer using `vi.spyOn`. This approach is explicit, test-scoped, and makes the scenario being tested clear. | |
| ```typescript | |
| import { vi } from "vitest"; | |
| import SettingsService from "#/api/settings-service/settings-service.api"; | |
| const getSettingsSpy = vi.spyOn(SettingsService, "getSettings"); | |
| getSettingsSpy.mockResolvedValue({ | |
| llm_model: "openai/gpt-4o", | |
| llm_api_key_set: true, | |
| // ... other settings | |
| }); | |
| ``` | |
| Use `mockResolvedValue` for success scenarios and `mockRejectedValue` for error scenarios: | |
| ```typescript | |
| getSettingsSpy.mockRejectedValue(new Error("Failed to fetch settings")); | |
| ``` | |
| ### Network Layer Mocking (Advanced) | |
| For tests that need actual network-level behavior (WebSockets, testing retry logic, etc.), use `server.use()` to override handlers per test. | |
| > [!IMPORTANT] | |
| > **Reuse the global server instance** - Don't create new `setupServer()` calls in individual tests. The project already has a global MSW server configured in `vitest.setup.ts` that handles lifecycle (`server.listen()`, `server.resetHandlers()`, `server.close()`). Use `server.use()` to add runtime handlers for specific test scenarios. | |
| ```typescript | |
| import { http, HttpResponse } from "msw"; | |
| import { server } from "#/mocks/node"; | |
| it("should handle server errors", async () => { | |
| server.use( | |
| http.get("/api/my-endpoint", () => { | |
| return new HttpResponse(null, { status: 500 }); | |
| }), | |
| ); | |
| // ... test code | |
| }); | |
| ``` | |
| For WebSocket testing, see `__tests__/helpers/msw-websocket-setup.ts` for utilities. | |
| ## Adding New API Mocks | |
| When adding new API endpoints, create mocks in both places to maintain 1:1 similarity with the backend: | |
| ### 1. Add to `src/mocks/` (for development) | |
| Create or update a domain-specific handler file: | |
| ```typescript | |
| // src/mocks/my-feature-handlers.ts | |
| import { http, HttpResponse } from "msw"; | |
| export const MY_FEATURE_HANDLERS = [ | |
| http.get("/api/my-feature", () => { | |
| return HttpResponse.json({ | |
| data: "mock response", | |
| }); | |
| }), | |
| ]; | |
| ``` | |
| Register in `handlers.ts`: | |
| ```typescript | |
| import { MY_FEATURE_HANDLERS } from "./my-feature-handlers"; | |
| export const handlers = [ | |
| // ... existing handlers | |
| ...MY_FEATURE_HANDLERS, | |
| ]; | |
| ``` | |
| ### 2. Mock in tests for specific scenarios | |
| In your test files, spy on the service method to control responses per test case: | |
| ```typescript | |
| import { vi } from "vitest"; | |
| import MyFeatureService from "#/api/my-feature-service.api"; | |
| const spy = vi.spyOn(MyFeatureService, "getData"); | |
| spy.mockResolvedValue({ data: "test-specific response" }); | |
| ``` | |
| See `__tests__/routes/llm-settings.test.tsx` for a real-world example of service layer mocking. | |
| > [!TIP] | |
| > For guidance on creating service APIs, see `src/api/README.md`. | |
| ## Best Practices | |
| - **Keep mocks close to real API contracts** - Update mocks when backend changes | |
| - **Use service layer mocking for most tests** - It's simpler and more explicit | |
| - **Reserve network layer mocking for integration tests** - WebSockets, retry logic, etc. | |
| - **Export mock data from handler files** - Reuse in tests (e.g., `MOCK_DEFAULT_USER_SETTINGS`) | |