CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

launchdarkly-testing

Use whenever code that reads LaunchDarkly feature flags needs tests: the TestData source for hermetic tests with no network, file-based fixtures, per-test flag overrides and assignment checks. Which flag combinations to cover is in openfeature-sdk-testing's flag test-matrix reference.

Quality

90%

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

The canonical home for this skill is testland/launchdarkly-testing

SKILL.md
Quality
Evals
Security

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 experiment-results-interpreter.
  • FileDataSource schema is LaunchDarkly-internal. Refer to LD docs for the format.

References

  • LaunchDarkly SDK docs: launchdarkly.com/docs/sdk.
  • TestData data source guide: launchdarkly.com/docs/sdk/features/test-data-sources.
  • File-based data source: launchdarkly.com/docs/sdk/features/flags-from-files.
  • Deciding WHICH flag combinations to cover is a separate step this skill does not do: when code branches on several flags, load openfeature-sdk-testing (its flag test-matrix reference) for the matrix, then come back here to pin each flag's value per test.
  • Sibling SDK umbrella: openfeature-sdk-testing (OpenFeature + Unleash / Flagsmith / GrowthBook references).
  • Cross-plugin: feature-flag-test-harness, ab-test-validity-checklist.
Repository

testland/qa

Path

plugins/qa-feature-flags/skills/launchdarkly-testing/SKILL.md

Last updated
First committed

Canonical home

testland/launchdarkly-testing
In sync

since Jul 23, 2026

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.