CtrlK
BlogDocsLog inGet started
Tessl Logo

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

77

Quality

97%

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:
launchdarkly-testing
description:
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.

launchdarkly-testing

Overview

LaunchDarkly's server-side SDK exposes a TestData data source that lets tests configure flag values without any network call - the canonical hermetic-test pattern. Per launchdarkly.com/docs/sdk, TestData replaces the production data source; everything else about the SDK (variation evaluation, targeting rules, default handling) runs the real code paths.

When to use

  • Unit / integration tests for code that calls client.variation() / client.boolVariation().
  • Tests asserting on targeting-rule behaviour (segment matching, percentage rollout).
  • Assignment-integrity tests per ab-test-validity-checklist.

Authoring

Install

npm install --save-dev launchdarkly-node-server-sdk
pip install launchdarkly-server-sdk

Initialize with TestData

import * as LaunchDarkly from 'launchdarkly-node-server-sdk';

const td = LaunchDarkly.TestData.dataSource();
const client = LaunchDarkly.init('sdk-test-key', {
  updateProcessor: td,
  sendEvents: false,         // No event uploads in tests
});

await client.waitForInitialization();

// Configure a flag's default + per-user variation
td.update(td.flag('show-new-ui').booleanFlag().on(true));

Variation evaluation

test('flag on returns true', async () => {
  td.update(td.flag('show-new-ui').booleanFlag().on(true));
  const user = { key: 'user-1' };
  const enabled = await client.variation('show-new-ui', user, false);
  expect(enabled).toBe(true);
});

test('flag off returns default', async () => {
  td.update(td.flag('show-new-ui').booleanFlag().on(false));
  const enabled = await client.variation('show-new-ui', { key: 'user-1' }, false);
  expect(enabled).toBe(false);
});

Targeting rules

test('only premium users get treatment', async () => {
  td.update(
    td.flag('premium-feature')
      .booleanFlag()
      .variationForUser('premium-user-1', true)
      .fallthroughVariation(false)
  );
  expect(await client.variation('premium-feature', { key: 'premium-user-1' }, false)).toBe(true);
  expect(await client.variation('premium-feature', { key: 'free-user-1' }, false)).toBe(false);
});

Percentage rollout (deterministic assignment)

test('rollout is deterministic per user', async () => {
  td.update(td.flag('half-rollout').booleanFlag().on(true).variations(true, false));
  // Bucketing is deterministic on user key
  const r1 = await client.variation('half-rollout', { key: 'user-1' }, false);
  const r2 = await client.variation('half-rollout', { key: 'user-1' }, false);
  expect(r1).toBe(r2);
});

File-based data source (alternative)

For fixture-shared tests:

const fileSource = LaunchDarkly.FileDataSource({
  paths: ['./tests/fixtures/ld-flags.json'],
});
const client = LaunchDarkly.init('sdk-test-key', {
  updateProcessor: fileSource,
  sendEvents: false,
});

The JSON file uses LaunchDarkly's flag-snapshot format. Useful for cross-language test fixtures.

Teardown

afterAll(async () => {
  await client.close();
});

Running

npm test

CI integration

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

No LAUNCHDARKLY_SDK_KEY needed in CI - TestData/file source replaces it.

Anti-patterns

Anti-patternWhy it failsFix
Real SDK key in testsTest traffic pollutes prod analyticsTestData / FileDataSource
sendEvents: true in testsEvent upload from CIsendEvents: false
Multiple init per testSlow init; leaksOne global client; TestData.update per test
Asserting exact variation result without td.updateRace vs SDK stateAlways update first
Forgetting await client.waitForInitialization()Race; variation returns defaultAlways wait
Sharing td across test filesCross-test pollutionPer-file or per-test TestData
No teardown client.close()Network handles leakAlways close in afterAll

Limitations

  • TestData is server-side-SDK-specific. Client-side SDKs have their own offline modes.
  • Doesn't validate LaunchDarkly's UI-side rollout math. Platform-side bucketing is separately tested.
  • No experiment-results validation. That's feature-flag-experiment-validator (in the qa-shift-right plugin).
  • FileDataSource schema is LaunchDarkly-internal. Refer to LD docs for the format.

References

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/launchdarkly-testing badge