It's time to reap the full benefits of services, layers and dependency injection.
We want to test our app. Since everything is composable we can create a setup specific for testing with its own Config
configuration.
Our objective is to verify that getPokemon
performs the correct request and returns the expected response. However, we don't want to make real requests to pokeapi.co
, but instead use a local mock server (localhost
).
Let's start by setting up our testing environment using vitest
and msw
:
vitest
: Testing frameworkmsw
: API mocking
Setup vitest and msw for testing
Let's start by installing them both:
pnpm install -D vitest msw
We use msw
to intercept and mock the response of HTTP requests. Instead of sending a request to a real server (https://pokeapi.co
), msw
intercepts requests and returns mock data, with the following benefits:
- Test app without interacting with any external service
- Verify correct behavior for all possible responses
msw
allows creating handlers that intercept a specific HTTP request and instead return a mocked response.
Let's create a new test
folder that contains handlers.ts
:
import { HttpResponse, http } from "msw";
import type { Pokemon } from "../src/schemas";
const mockPokemon: Pokemon = {
id: 1,
height: 10,
weight: 10,
order: 1,
name: "myname",
};
export const handlers = [
// 👇 Intercept requests for any pokemon (using wildcard `*`)
http.get("http://localhost:3000/api/v2/pokemon/*", () => {
return HttpResponse.json(mockPokemon);
}),
];
handlers
is a list of requests that are intercepted by msw
. Instead of making the full request msw
will return the mock response we defined (mockPokemon
).
Setup up testing with node
The last step of this setup is creating a testing server for node.
We create a new test/node.ts
file that exports the result of calling setupServer
from msw/node
with our custom handlers
:
import { setupServer } from "msw/node";
import { handlers } from "./handlers";
export const server = setupServer(...handlers);
msw
requires to open, reset and close the server we defined above. We can do this inside beforeAll
, afterEach
and afterAll
in a new index.test.ts
file:
import { afterAll, afterEach, beforeAll } from "vitest";
import { server } from "../test/node";
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
The final step is adding a test
command to run our test with vitest
:
{
"name": "effect-getting-started-course",
"version": "1.0.0",
"main": "src/index.js",
"author": "Sandro Maglione",
"license": "MIT",
"scripts": {
"dev": "BASE_URL=https://pokeapi.co tsx src/index.ts",
"test": "vitest"
},
"devDependencies": {
"@types/node": "^20.14.10",
"msw": "^2.3.1",
"tsx": "^4.16.2",
"typescript": "^5.5.3",
"vitest": "^2.0.2"
},
"dependencies": {
"@effect/platform": "^0.69.2",
"effect": "^3.10.0"
}
}