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.
Install with skills.sh (any agent)
npx skills add testland/qa --skill unleash-testingunleash-testing
When to use
Authoring
Install
npm install --save-dev unleash-client
pip install UnleashClientBootstrap with toggles (offline)
import { initialize } from 'unleash-client';
const unleash = initialize({
url: 'http://localhost:4242/api/',
appName: 'test-app',
disableMetrics: true, // No metrics upload
disablePolling: true, // No background polling
bootstrap: {
data: [
{
name: 'show-new-ui',
enabled: true,
strategies: [
{ name: 'default' },
],
},
],
},
});
// Wait for initialization
await new Promise<void>((resolve) => unleash.once('synchronized', resolve));The bootstrap.data array is the SDK's initial flag state; since polling is disabled, that state persists for the test session.
isEnabled tests
test('flag enabled', () => {
expect(unleash.isEnabled('show-new-ui')).toBe(true);
});
test('flag with context', () => {
expect(unleash.isEnabled('premium-only', { userId: 'u1', properties: { tier: 'premium' } })).toBe(true);
});Custom strategy
Unleash's extensibility: custom strategies implement an isEnabled(parameters, context) method.
import { Strategy } from 'unleash-client';
class TenantStrategy extends Strategy {
constructor() { super('tenantStrategy'); }
isEnabled(parameters: any, context: any): boolean {
const allowedTenants = (parameters.tenants ?? '').split(',');
return allowedTenants.includes(context.tenantId);
}
}
const unleash = initialize({
url: '...',
appName: 'test',
strategies: [new TenantStrategy()],
bootstrap: {
data: [
{
name: 'flag-x',
enabled: true,
strategies: [{ name: 'tenantStrategy', parameters: { tenants: 'A,B' } }],
},
],
},
});
test('strategy allows tenant A', () => {
expect(unleash.isEnabled('flag-x', { tenantId: 'A' })).toBe(true);
});
test('strategy rejects tenant C', () => {
expect(unleash.isEnabled('flag-x', { tenantId: 'C' })).toBe(false);
});Percentage-rollout determinism
const unleash = initialize({
// ...
bootstrap: {
data: [
{
name: 'gradual-rollout',
enabled: true,
strategies: [{ name: 'flexibleRollout', parameters: { rollout: '50', stickiness: 'userId' } }],
},
],
},
});
test('rollout deterministic per user', () => {
const r1 = unleash.isEnabled('gradual-rollout', { userId: 'u1' });
const r2 = unleash.isEnabled('gradual-rollout', { userId: 'u1' });
expect(r1).toBe(r2);
});Teardown
afterAll(() => unleash.destroy());Running
npm testCI integration
jobs:
unleash-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
- run: npm ci && npm testFully offline - no Unleash server URL needed since polling is disabled.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
disableMetrics: false in CI | Spurious metrics POSTs | Always disableMetrics: true in tests |
disablePolling: false without test-only URL | Network calls; CI flakes | Always disable polling in offline tests |
bootstrap.data is stale | Drift from prod definitions | Pull from Unleash periodically; commit fixture |
| Custom strategies not unit-tested | Logic bugs in the strategy itself | Test the strategy class in isolation too |
Skipping synchronized event wait | Race: isEnabled returns default | Always await synchronized |
Forgetting unleash.destroy() | Goroutine / timer leak | Always destroy in afterAll |
| Tests use real Unleash server | Slow; flaky if server down | Bootstrap mode |
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.
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.
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.