repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

starfx / docs / posts
Eric Bower · 14 Feb 24

testing.md

 1---
 2title: Testing
 3description: You don't need an HTTP interceptor
 4---
 5
 6Need to write tests? Use libraries like `msw` or `nock`? Well you don't need
 7them with `starfx`. If the `mdw.fetch()` middleware detects `ctx.response` is
 8already filled then it skips making the request. Let's take the update user
 9endpoint example and provide stubbed data for our tests.
10
11```tsx
12import { fireEvent, render, screen } from "@testing-library/react";
13import { useDispatch, useSelector } from "starfx/react";
14import { db } from "./schema.ts";
15import { updateUser } from "./user.ts";
16
17function UserSettingsPage() {
18  const id = "1";
19  const dispatch = useDispatch();
20  const user = useSelector((state) => db.users.selectById(state, { id }));
21
22  return (
23    <div>
24      <div>Name: {user.name}</div>
25      <button onClick={() => dispatch(updateUser({ id, name: "bobby" }))}>
26        Update User
27      </button>
28    </div>
29  );
30}
31
32describe("UserSettingsPage", () => {
33  it("should update the user", async () => {
34    // just for this test -- inject a new middleware into the endpoint stack
35    updateUser.use(function* (ctx, next) {
36      ctx.response = new Response(
37        JSON.stringify({ id: ctx.payload.id, name: ctx.payload.name }),
38      );
39      yield* next();
40    });
41
42    render(<UserSettingsPage />);
43
44    const btn = await screen.findByRole("button", { name: /Update User/ });
45    fireEvent.click(btn);
46
47    await screen.findByText(/Name: bobby/);
48  });
49});
50```
51
52That's it. No need for http interceptors and the core functionality works
53exactly the same, we just skip making the fetch request for our tests.
54
55What if we don't have an API endpoint yet and want to stub the data? We use the
56same concept but inline inside the `updateUser` endpoint:
57
58```ts
59export const updateUser = api.post<{ id: string; name: string }>(
60  "/users/:id",
61  [
62    function* (ctx, next) {
63      ctx.request = ctx.req({
64        body: JSON.stringify({ name: ctx.payload.name }),
65      });
66      yield* next();
67    },
68    function* (ctx, next) {
69      ctx.response = new Response(
70        JSON.stringify({ id: ctx.payload.id, name: ctx.payload.name }),
71      );
72      yield* next();
73    },
74  ],
75);
76```
77
78Wow! Our stubbed data is now colocated next to our actual endpoint we are trying
79to mock! Once we have a real API we want to hit, we can just remove that second
80middleware function and everything will work exactly the same.