growthbook-testing
Wraps GrowthBook Node SDK testing patterns: GrowthBookClient initialization with direct payload (initSync; no network), isOn / getFeatureValue / evalFeature, scoped instances (createScopedInstance) for per-request user context, inline experiment (runInlineExperiment) tests, and tracking-callback assertion patterns. Use when writing tests for code using GrowthBook for flags + experiments.
Install with skills.sh (any agent)
npx skills add testland/qa --skill growthbook-testinggrowthbook-testing
Overview
Per docs.growthbook.io/lib/node (opens in new window), the GrowthBook Node SDK's GrowthBookClient supports an initSync({ payload }) pattern that fully bypasses the network - pass the feature definitions directly.
createScopedInstance lets each test (or request) bind its own user context cleanly.
When to use
Authoring
Install
npm install --save-dev @growthbook/growthbookInitialize with payload (offline)
Per docs.growthbook.io (opens in new window):
import { GrowthBookClient } from '@growthbook/growthbook';
const gbClient = new GrowthBookClient().initSync({
payload: {
features: {
'show-new-ui': { defaultValue: true },
'experiment-x': {
defaultValue: false,
rules: [{ condition: { id: 'test-user-1' }, force: true }],
},
},
},
});initSync is purpose-built for tests - no async wait.
isOn / getFeatureValue
test('flag on', () => {
const userContext = { attributes: { id: 'user-1' } };
expect(gbClient.isOn('show-new-ui', userContext)).toBe(true);
});
test('typed feature value', () => {
const userContext = { attributes: { id: 'user-1' } };
const color = gbClient.getFeatureValue('button-color', 'blue', userContext);
expect(['blue', 'red', 'green']).toContain(color);
});Scoped instance per test
Per GrowthBook docs:
test('user-specific evaluation', () => {
const instance = gbClient.createScopedInstance({
attributes: { id: 'user-1', plan: 'premium' },
});
expect(instance.isOn('premium-feature')).toBe(true);
});Avoids passing context everywhere.
Inline experiments
test('inline experiment returns one variant', () => {
const userContext = { attributes: { id: 'user-1' } };
const { value } = gbClient.runInlineExperiment({
key: 'my-experiment',
variations: ['red', 'blue', 'green'],
coverage: 1.0,
weights: [0.33, 0.34, 0.33],
}, userContext);
expect(['red', 'blue', 'green']).toContain(value);
});Tracking-callback assertions
test('experiment tracking fires', () => {
const tracked: any[] = [];
const client = new GrowthBookClient({
trackingCallback: (exp, result, ctx) => {
tracked.push({ key: exp.key, variation: result.key });
},
}).initSync({ payload: {} });
client.runInlineExperiment(
{ key: 'exp-x', variations: [0, 1] },
{ attributes: { id: 'user-1' } }
);
expect(tracked).toHaveLength(1);
expect(tracked[0].key).toBe('exp-x');
});Feature-usage callback
const evaluated: any[] = [];
test('feature usage logged', () => {
const userContext = {
attributes: { id: 'user-1' },
onFeatureUsage: (key: string, result: any) => {
evaluated.push({ key, value: result.value });
},
};
gbClient.evalFeature('feature-x', userContext);
expect(evaluated).toContainEqual(expect.objectContaining({ key: 'feature-x' }));
});TypeScript-strict feature contract
interface AppFeatures {
'button-color': string;
'font-size': number;
'newForm': boolean;
}
const gbClient = new GrowthBookClient<AppFeatures>({}).initSync({ payload: {} });
const color = gbClient.getFeatureValue('button-color', 'blue', ctx); // typed
// gbClient.isOn('buton-color', ctx); // typo → compile errorRunning
npm testCI integration
jobs:
growthbook-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
- run: npm ci && npm testFully offline; no GrowthBook key needed.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Live apiHost in tests | Network requests; flaky | initSync({ payload }) |
| Skipping tracking-callback assertion | Exposure-event regressions silent | Per-test callback + assert |
| Sharing scopedInstance across tests | Cross-test state | Per-test create |
| TypeScript any-typed features | Lose compile-time safety | Generic AppFeatures |
coverage: 0.1 in test without large N | Not enough samples to see all variations | Use coverage: 1.0 for deterministic tests |
| Missing user.id in context | Bucketing degenerate | Always pass attributes.id |
Tests assume default weights is 50/50 | Default behaviour drifts | Explicit weights |
Limitations
References
Related skills
feature-flag-test-matrix-reference
Pure-reference catalog of feature-flag test matrix design. Defines the flag-state combinatorics problem (N flags × M variants × K user-segments = N×M×K test cases), the canonical coverage strategies (pairwise interaction coverage; default-only smoke; full matrix; risk-driven matrix), the kill-switch + percentage-rollout test patterns, and the relationship between flags + experiments (flags toggle behaviour; experiments measure outcome). Use when designing the flag-test surface for a new project or auditing existing flag-test coverage.
flag-removal-runbook-author
Workflow-driven skill that builds the runbook for safely removing a feature flag from the codebase + the flag platform. Walks through: pre-removal verification (flag fully rolled out, no usage variance in evaluations, dependent code paths identified), the code-removal steps (delete the if-branches, simplify, restore types), the platform-side removal (archive in LaunchDarkly / Unleash / Flagsmith / GrowthBook), the verification post-removal, and the rollback plan. Use when removing a flag that has finished its mission (rollout-complete, experiment-shipped, kill-switch retired).
flag-state-coverage-builder
Workflow-driven skill that builds a flag-state coverage matrix from the project's flag inventory and risk register. Walks through: inventorying flags (grep for flag-evaluation calls), classifying each (boolean / multi-variant / kill-switch / experiment), choosing the coverage strategy (per-flag-isolation / pairwise / full / risk-driven per feature-flag-test-matrix-reference), generating the test matrix (PICT for pairwise; manual for risk-driven), and emitting test skeletons. Use when introducing flag-test coverage to a new codebase or when a flag-related incident exposes a coverage gap.
flagsmith-testing
Wraps Flagsmith server-side SDK testing patterns (feature flags / feature toggles) so tests run without calling the Flagsmith API: offline mode with LocalFileHandler + a downloaded environment.json snapshot, local-evaluation mode (no per-request network), and default_flag_handler for per-feature mocked fallbacks, via the get_environment_flags / get_identity_flags evaluation paths. Use when writing feature-flag tests for code that uses Flagsmith, mocking flag values, or testing feature toggles offline in CI.
killswitch-test-author
Workflow-driven skill that authors the four test categories specific to kill-switch (ops-toggle) flags: switch-OFF graceful degradation, fail-static default when the flag service is unreachable, latency budget for the kill decision, and no-data-corruption mid-flight. Distinct from flag-state-coverage-builder (which builds a full coverage matrix across all flag types) and feature-flag-test-matrix-reference (which catalogs patterns without producing tests). Use when a kill-switch flag exists in the codebase and needs dedicated, production-incident-rehearsing tests authored for it.
launchdarkly-testing
Wraps LaunchDarkly server-side SDK testing patterns: TestData data source for hermetic tests (no network), file-based data source for fixture-driven tests, flag override patterns (TestData.update for per-test flag values), and assignment-integrity tests. Use when writing tests for code that uses LaunchDarkly flags.
openfeature-sdk-testing
Wraps OpenFeature (CNCF vendor-neutral SDK abstraction) testing patterns: the InMemoryProvider for hermetic tests without network calls, provider registration via OpenFeature.setProvider, the getBooleanValue/getBooleanDetails evaluation API with EvaluationDetails (value, variant, reason, errorCode), hooks for evaluation side-effects, and evaluation context for targeting-rule tests. Covers TypeScript, Java, and Python SDKs. Use when writing tests for code that resolves feature flags through the OpenFeature SDK regardless of the underlying flag management platform.
unleash-testing
Wraps Unleash (Open Source / SaaS) SDK testing patterns: bootstrap with a static toggles array (no network), the test mode (disableMetrics + disablePolling), the custom strategy testing pattern (implement a Strategy class + assert isEnabled), and assignment-integrity tests. Use when writing tests for code that uses Unleash for feature flags.