Umbrella for experimentation-SDK test harnesses: the shared offline-datafile / hermetic-init pattern (commit a point-in-time flag/experiment config fixture, initialize the SDK with no network, pin arms per test, assert assignment integrity), with per-vendor references for Statsig (localMode + overrideGate), Optimizely (datafile + forced decisions), Split.io / Harness FME (localhost mode + features map or YAML fixture), Amplitude Experiment (local evaluation + bootstrap), and VWO (settings file + deterministic bucketing). Use when writing tests for application code instrumented with any of these five experimentation SDKs; for experiment DESIGN gates use ab-test-validity-checklist, and to read results use experiment-results-interpreter.
76
96%
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
Optimizely Feature Experimentation (Optimizely Full Stack / Optimizely X) uses a datafile - a JSON blob describing all flags, experiments, and audiences - that the SDK fetches and evaluates locally. Per docs.developers.optimizely.com/feature-experimentation/docs/python-sdk, the SDK supports datafile-based testing: load a fixture datafile in tests, no network call.
The current API surface is decide (single flag) / decideAll
(all flags) per the v5 SDK.
pip install optimizely-sdk, or the npm package).tests/fixtures/.OptimizelyUserContext per test with the attributes the audience targeting needs.user.decide("new_checkout_flow").enabled is True - before pinning arms. If it fails, the datafile is stale or missing the flag; re-export it and re-run.set_forced_decision where a test needs a fixed variation, then call decide (one flag) or decide_all (all flags).variation_key / enabled (never variation IDs); add assignment-integrity and event-tracking checks via the notification listener (see optimizely-recipes.md).pip install optimizely-sdk # Python
npm install --save-dev @optimizely/optimizely-sdkPer Optimizely docs, the datafile path is the canonical offline approach:
import json
from optimizely import optimizely
# Load a checked-in datafile fixture
with open("tests/fixtures/optimizely-datafile.json") as f:
datafile = json.load(f)
client = optimizely.Optimizely(json.dumps(datafile))The datafile is downloadable from the Optimizely UI or via the Optimizely API; commit a version-specific copy to the repo for deterministic tests.
def test_get_decision_for_user():
user = client.create_user_context("user-1", {"plan": "premium"})
decision = user.decide("new_checkout_flow")
assert decision.enabled is True
assert decision.variation_key == "treatment_a"from optimizely.optimizely_user_context import OptimizelyDecisionContext
def test_force_user_to_treatment():
user = client.create_user_context("user-1")
context = OptimizelyDecisionContext(flag_key="new_checkout_flow", rule_key=None)
user.set_forced_decision(context, OptimizelyForcedDecision(variation_key="treatment_a"))
decision = user.decide("new_checkout_flow")
assert decision.variation_key == "treatment_a"Extended recipes - assignment-integrity and event-tracking tests, the pytest run command, and CI integration yaml - are in optimizely-recipes.md.
The team ships a new_checkout_flow flag with a treatment_a
variation, and QA needs a deterministic test that a premium-plan
user is routed into the treatment. Follow the How-to-use steps:
export the fixture, init offline, create the {"plan": "premium"}
context, assert decide("new_checkout_flow") returns enabled is True / variation_key == "treatment_a", then pin the arm with
set_forced_decision for the variant-specific path.
Result: a deterministic pass/fail on the checkout-routing logic with no network round-trip and no SDK key - the fixture drives the whole decision.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Tests with live SDK key | Production data polluted; rate-limited | Use datafile fixture |
| Datafile not version-controlled | Tests flake when prod config changes | Commit the fixture |
| Forced decisions leak across tests | Cross-test pollution | Per-test user context; reset before assertion |
Skipping client.shutdown / network listener cleanup | Goroutine / handle leak | Always cleanup |
| Asserting on variation IDs not keys | IDs change per environment | Use variation_key |
| Manual event tracking in tests vs notification listener | Misses platform-emitted events | Use the listener |
| Tests rely on real decide-network roundtrip | Slow; non-deterministic | Datafile + offline |
ab-test-validity-checklist; result-trust references (peeking, guardrails) live in experiment-results-interpreter.