/Docs
IntroductionHow FeatureGate worksQuickstart
OrganisationsProjectsEnvironments
OverviewTypes and valuesConfigure across environmentsTargeting rulesPercentage rolloutsDesign evaluation contextLifecycle and intentTags and discovery
Choose an evaluation approachEvaluation credentialsNode.js SDKEvaluate over HTTPEvaluate in browsersSnapshots and configuration freshnessErrors and fallbacksTest flags locallyProduction integration checklist
Run a controlled rolloutKill switchesProject insightsActivity and audit historyReview and clean up flagsEnvironment readinessTroubleshooting and limits
Members, roles, and permissionsInvite and manage a teamOrganisation settings and operational defaultsProject lifecycle and transferPlans, billing, and team organisationsOrganisation closure and reactivationAccount settings and security
Management API overviewPreviewProvision flags through automationPreviewRotate runtime keys through automationPreviewAudit project changes through automationPreview
DocsAPI Reference

Evaluate/Test flags locally

Get access
/Docs
Get access
  1. Docs
  2. Evaluate
  3. Test flags locally

Test flags locally

Exercise flag branches without production credentials or network requests.

MarkdownFeedback
PreviousErrors and fallbacksNextProduction integration checklist

On this page

Create an in-memory clientTest every meaningful branchUse deterministic contextsIsolate testsIntegration testsTest cleanup itselfCommon mistakes

The Node.js SDK accepts an in-memory snapshot. Tests and local tools can evaluate immediately without a runtime key, network request, or initialize().

Use local snapshots for unit tests and deterministic developer workflows. Use a dedicated hosted environment only when the test must verify the real API or snapshot contract.

Create an in-memory client

import { FeatureGate } from "@featuregate/node";

const featuregate = new FeatureGate({
  flags: {
    "new-checkout": { defaultValue: true },
  },
});

expect(featuregate.getBooleanValue("new-checkout", false)).toBe(true);

Pure in-memory clients are ready immediately. They do not poll and do not need remote initialization.

Test every meaningful branch

For each guarded decision, test:

  • The configured new value.
  • The configured existing value.
  • The caller default when the flag is absent.
  • A wrong getter type when that failure is important.
  • Matching and non-matching targeting context.
  • Missing attributes used by a rule.

Test business behaviour rather than the SDK implementation. An application test should verify what the checkout does when enabled, not only that a getter returned true.

Use deterministic contexts

Percentage allocation is deterministic only for stable inputs. Use fixed opaque identifiers in tests and assert the observed branch.

Do not generate random rollout IDs inside a test. A random value can move between buckets and create flaky results that reveal nothing about application correctness.

Keep context fixtures small and explicit:

const staffContext = {
  targetingKey: "user_test_staff",
  attributes: {
    user: { id: "user_test_staff", role: "staff" },
    account: { id: "account_test", plan: "pro" },
  },
};

Isolate tests

Create a client inside each test or fixture. Do not mutate one shared snapshot across tests because order-dependent state can hide cleanup and targeting mistakes.

Close any client configured with polling. Pure in-memory clients do not own network resources, but a consistent cleanup pattern keeps fixtures safe if their configuration changes later.

Do not reuse production flag keys merely because they are familiar. Test fixtures should describe the application contract and remain independent from production configuration.

Integration tests

Use a dedicated non-production environment when validating authentication, snapshot parsing, ETag refresh, hosted targeting, or the HTTP response contract.

Create a minimum-scope credential and inject it through CI secrets. Never print it on failure. Do not use production credentials or permit CI to mutate production configuration.

Tests that create or change hosted resources must restore or delete their resources. Record enough metadata to clean up after interruption without logging raw credentials.

Test cleanup itself

Temporary flags should disappear from tests when guarded code is removed. A fixture that still needs the retired key is evidence that cleanup is incomplete.

Add assertions for the final unguarded behaviour before deleting obsolete flag branches. Then remove the test variants that only existed to exercise the retired branch.

Common mistakes

  • Calling initialize() on an in-memory-only client when no remote load is needed.
  • Sharing mutable clients across parallel tests.
  • Using random rollout identifiers.
  • Treating caller-default tests as proof that hosted configuration is correct.
  • Putting a production key in a local .env or CI log.

Related pages: Node.js SDK, Design evaluation context, and Production integration checklist.