FlagFix
SDKs

Unit testing

Mock the network with fetchImpl. Keep tests offline and deterministic.

Last updated on

FlagFix

Goals

  • Assert flag reads and event queueing without a live API
  • Keep tests fast and hermetic (CI-friendly)
  • Avoid leaking real SDK keys into fixtures

Browser / Node pattern

Pass a fake fetchImpl that returns the same JSON shapes as the runtime OpenAPI (EvaluateAllResponse, ingest 202, etc.).

import { createFlagFix } from "@flagfix/node";
import { describe, it, expect } from "vitest";

describe("checkout flag", () => {
  it("reads enabled from evaluate-all", async () => {
    const client = createFlagFix({
      serverKey: "test-server-key",
      apiHost: "http://flagfix.test",
      lifecycle: false, // browser only: disable document listeners
      fetchImpl: async (input) => {
        const url = String(input);
        if (url.includes("/evaluate-all") || url.includes("/evaluate-public")) {
          return new Response(
            JSON.stringify({
              flags: [
                {
                  key: "new_checkout",
                  enabled: true,
                  value: null,
                  configurationVersion: 1,
                },
              ],
              matchedSegments: [],
              configurationVersion: 1,
            }),
            { status: 200, headers: { "content-type": "application/json" } },
          );
        }
        if (url.includes("/ingest")) {
          return new Response(JSON.stringify({ acceptedIds: ["1"] }), {
            status: 202,
            headers: { "content-type": "application/json" },
          });
        }
        return new Response("not mocked", { status: 500 });
      },
    });

    await client.start();
    expect(client.hasFeature("new_checkout")).toBe(true);
  });
});

React

  • Wrap components in FlagFixProvider with a pre-built client (or mocked options + fetchImpl)
  • Set lifecycle: false on browser options inside tests
  • Prefer serverFlagState fixtures to assert first paint without waiting on network
import { FlagFixProvider, useHasFeature } from "@flagfix/react";
import { render, screen } from "@testing-library/react";

function Banner() {
  const on = useHasFeature("new_checkout");
  return <div>{on ? "on" : "off"}</div>;
}

it("renders SSR snapshot", () => {
  render(
    <FlagFixProvider
      options={{ clientKey: "test", apiHost: "http://flagfix.test", lifecycle: false }}
      serverFlagState={{
        flags: { new_checkout: { key: "new_checkout", enabled: true, value: null } },
        configurationVersion: 1,
        source: "ssr",
      }}
    >
      <Banner />
    </FlagFixProvider>,
  );
  expect(screen.getByText("on")).toBeInTheDocument();
});

What not to do

  • Do not point unit tests at production or shared staging
  • Do not assert on wall-clock flush timing without controlling the clock
  • Do not import @flagfix/sdk-core. Test through @flagfix/browser, @flagfix/node, or @flagfix/react

Related: How to test flag evaluation

Was this page useful?

On this page