CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/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.

80

Quality

100%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
unleash-testing
description:
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.

unleash-testing

When to use

  • Tests for code that calls unleash.isEnabled(flagName, context).
  • Tests for custom Unleash strategies (the extensibility point).
  • Assignment-integrity tests per ab-test-validity-checklist.

Authoring

Install

npm install --save-dev unleash-client
pip install UnleashClient

Bootstrap 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 test

CI integration

jobs:
  unleash-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
      - run: npm ci && npm test

Fully offline - no Unleash server URL needed since polling is disabled.

Anti-patterns

Anti-patternWhy it failsFix
disableMetrics: false in CISpurious metrics POSTsAlways disableMetrics: true in tests
disablePolling: false without test-only URLNetwork calls; CI flakesAlways disable polling in offline tests
bootstrap.data is staleDrift from prod definitionsPull from Unleash periodically; commit fixture
Custom strategies not unit-testedLogic bugs in the strategy itselfTest the strategy class in isolation too
Skipping synchronized event waitRace: isEnabled returns defaultAlways await synchronized
Forgetting unleash.destroy()Goroutine / timer leakAlways destroy in afterAll
Tests use real Unleash serverSlow; flaky if server downBootstrap mode

Limitations

  • Bootstrap is point-in-time. Drift between fixture + Unleash UI is invisible.
  • Custom strategies tested in isolation lose context. Integration test with the full SDK still needed.
  • Metric-side tests need real server. If you're testing the metrics export path, bootstrap mode is wrong.
  • Stickiness is parameter-driven. A misconfigured stickiness parameter shows up in production, not in tests.

References

  • Unleash docs: docs.getunleash.io.
  • Node SDK: docs.getunleash.io/sdks/node.
  • Custom strategies: docs.getunleash.io/concepts/activation-strategies.
  • Companion: feature-flag-test-matrix-reference.
  • Sibling SDKs: launchdarkly-testing, flagsmith-testing, growthbook-testing.
Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/unleash-testing badge