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, plus per-vendor hermetic-bootstrap references for Unleash (bootstrap toggles), Flagsmith (offline LocalFileHandler), and GrowthBook (initSync payload). Use when writing tests for code that resolves feature flags through the OpenFeature SDK or the Unleash / Flagsmith / GrowthBook native SDKs; LaunchDarkly has its own skill (launchdarkly-testing).
72
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.
npm install --save-dev unleash-client
pip install UnleashClientimport { 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.
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);
});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);
});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);
});afterAll(() => unleash.destroy());Fully offline - no Unleash server URL needed since polling is disabled:
jobs:
unleash-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
- run: npm ci && npm test| 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 |
stickiness
parameter shows up in production, not in tests.