Testing
Use Shipeasy.configurefortesting — the test-mode sibling of Shipeasy.configure. It does zero network, needs no api key, and seeds the values your code under…
Generated from the SDK's own /docs/ — also served raw at
https://shipeasy-ai.github.io/sdk-ruby/pages/testing.md.
Use Shipeasy.configure_for_testing — the test-mode sibling of
Shipeasy.configure. It does zero network, needs no api
key, and seeds the values your code under test should see via override arguments.
Then read through the ordinary Shipeasy::Client.new(user) — the same call
your production code uses.
require "shipeasy-sdk"
Shipeasy.configure_for_testing(
flags: { "new_checkout" => true },
configs: { "billing_copy" => { "title" => "Welcome" } },
experiments: { "checkout_button" => ["treatment", { "color" => "green" }] },
)
# construct once per callsite (cheap; binds the user)
client = Shipeasy::Client.new({ "user_id" => "u_123" })
client.get_flag("new_checkout") # => true
client.get_config("billing_copy") # => { "title" => "Welcome" }
result = client.get_experiment("checkout_button", { "color" => "blue" })
result.in_experiment # => true
result.group # => "treatment"
result.params # => { "color" => "green" }
# track / log_exposure are no-ops in test mode — safe to call, send nothing
client.track("purchase", { amount: 49 })Override argument shapes:
flags—{ name => bool }forcedget_flagresults.configs—{ name => value }forcedget_configresults (adecodestill applies).experiments—{ name => [group, params] }forced enrolments.
configure_for_testing replaces any previously-configured engine, so each
test can reconfigure freely (no reset boilerplate, unlike configure's
first-config-wins).
Quick overrides on the spot
Seeding up front isn't always enough — sometimes you want to flip one value
mid-test. The package-level override helpers do exactly that, layered on top of
whatever configure_for_testing / configure_for_offline (or even a live
configure) set up. They win until clear_overrides:
Shipeasy.configure_for_testing(flags: { "new_checkout" => true })
# …later, in one test, flip values without reconfiguring:
Shipeasy.override_flag("new_checkout", false) # name, value
Shipeasy.override_config("billing_copy", { "title" => "B" }) # name, value
Shipeasy.override_experiment("checkout_button", "control", { "color" => "blue" })
Shipeasy::Client.new({ "user_id" => "u_1" }).get_flag("new_checkout") # => false
Shipeasy.clear_overrides # drop every on-the-spot override| helper | effect |
|---|---|
Shipeasy.override_flag(name, value) | force get_flag(name) → value |
Shipeasy.override_config(name, value) | force get_config(name) → value |
Shipeasy.override_experiment(name, group, params) | force enrolment in group with params |
Shipeasy.clear_overrides | drop all of the above |
(These require a prior configure* call — they raise Shipeasy::Error otherwise.)
clear_overrides drops every override — including the values you passed to
configure_for_testing (which seeds through the same mechanism, and test mode has
no blob underneath). Under configure_for_offline it instead reverts to the
snapshot. To get a clean known state, call configure_for_testing(...) again.
Offline snapshot
Use Shipeasy.configure_for_offline to run fully offline against a real blob
— evaluations run the real eval logic (targeting, rollout, bucketing), no
network is touched, and the override args still apply on top:
Shipeasy.configure_for_offline(path: "shipeasy-snapshot.json")
client = Shipeasy::Client.new({ "user_id" => "u_123" })
client.get_flag("new_checkout")A snapshot file that works
A snapshot is
{ "flags": <body of /sdk/flags>, "experiments": <body of /sdk/experiments> }.
The shapes are name-keyed maps. Save this as shipeasy-snapshot.json — it
evaluates exactly as written:
{
"flags": {
"gates": {
"new_checkout": { "enabled": true, "rolloutPct": 10000, "salt": "new_checkout", "rules": [] },
"beta_banner": { "enabled": false, "rolloutPct": 0, "salt": "beta_banner", "rules": [] }
},
"configs": {
"billing_copy": { "value": { "title": "Welcome back", "cta": "Upgrade" } },
"upload_limits": { "value": { "max_mb": 50 } }
},
"killswitches": {
"payments_circuit_breaker": { "killed": false }
}
},
"experiments": { "experiments": {}, "universes": {} }
}- A gate is
{ "enabled", "rolloutPct" (0–10000, basis points), "salt", "rules": [] }.rolloutPct: 10000= 100% on;0= off for everyone. Add targeting underrules. - A config is
{ "value": <any JSON> };get_config("billing_copy")returns thatvalue. - A kill switch is
{ "killed": <bool>, "switches"?: { ... } }. - Leave
experimentsas{ "experiments": {}, "universes": {} }if you have none (or paste a real/sdk/experimentsbody).
Shipeasy.configure_for_offline(path: "shipeasy-snapshot.json")
c = Shipeasy::Client.new({ "user_id" => "u_1" })
c.get_flag("new_checkout") # => true (100% rollout)
c.get_flag("beta_banner") # => false (0% rollout)
c.get_config("billing_copy")["cta"] # => "Upgrade"
c.get_config("upload_limits", ->(v) { v["max_mb"] }) # => 50
c.get_killswitch("payments_circuit_breaker") # => falseYou can also pass the same structure inline as snapshot: instead of a file,
and layer overrides on top:
Shipeasy.configure_for_offline(
snapshot: { "flags" => { "gates" => {}, "configs" => {} }, "experiments" => {} },
flags: { "new_checkout" => true }, # same override args as configure_for_testing
)Tip: to capture a real production snapshot, save the bodies of the
GET /sdk/flagsandGET /sdk/experimentsresponses under those two keys.
Both helpers take the same attributes: transform as configure, so your
user-object mapping is exercised in tests exactly as in production.
Error reporting — `see()`
This SDK has see() — a structured error-reporting surface: every handled exception documents its product consequence, not just its stack. The report is a…
OpenFeature provider
The Ruby SDK ships an OpenFeature server provider, Shipeasy::OpenFeature::Provider, so apps standardised on the CNCF OpenFeature API can plug Shipeasy in as…