Flipper Usage In Specs
Last Updated:
Platform is enforcing Project/ForbidFlipperToggleInSpecs to stop new Flipper.enable and Flipper.disable usage in Vets-API specs.
Decision
Specs should not call:
Flipper.enable(...)Flipper.disable(...)
Specs should instead stub Flipper.enabled? with the same arguments the code under test uses.
Scope
Existing specs that use Flipper.enable to control flags for logic not related to flipper.
Out of Scope: Specs whose subject is the toggle operation or side effects of it, since stubbing would break the test.
Why?
Direct mutation of global Flipper state makes tests harder to reason about and can contribute to order-dependent behavior.
In vets-api, Flipper initialization also performs environment-specific setup during boot (see config/initializers/flipper.rb), which can make direct global mutation less predictable than local stubs.
Recommended patterns
Prefer stubbing Flipper.enabled? in each test context.
Single flag example
allow(Flipper).to receive(:enabled?).with(:my_feature).and_return(true)
Flag with actor/user example
allow(Flipper).to receive(:enabled?).with(:my_feature, user).and_return(true)
Multiple flag example
before do
allow(Flipper).to receive(:enabled?).with(:feature_a).and_return(true)
allow(Flipper).to receive(:enabled?).with(:feature_b).and_return(false)
end
Multiple flags with fall-through to real behavior
If other features may be called in the same test, let unspecified flags fall through:
before do
allow(Flipper).to receive(:enabled?).and_call_original
allow(Flipper).to receive(:enabled?).with(:feature_name).and_return(true)
end
Optional helper pattern
If you want to handle multiple values more directly than having multiple statements, or if you feel stubbing is getting repetitive in your tests, here’s an example of how you can create a helper.
def stub_flipper_flags(flag_values, call_original: false)
# Without call_original: true, flags not listed in flag_values hit the real adapter.
allow(Flipper).to receive(:enabled?).and_call_original if call_original
flag_values.each do |flag, value|
allow(Flipper).to receive(:enabled?).with(flag).and_return(value)
end
end
# usage
stub_flipper_flags({ feature_a: true, feature_b: false })
stub_flipper_flags({ feature_name: true }, call_original: true)
If your code calls Flipper.enabled? with an actor argument, mirror that signature in your stubs:
allow(Flipper).to receive(:enabled?).with(:my_feature, user).and_return(true)
Migration guidance
Rollout model: legacy callsites are currently suppressed inline so the Platform can enforce this for new code without a large one-time refactor. Done in PR #28509.
Existing violations are currently allowed with inline suppression so CI can enforce this for new code immediately:
Flipper.enable(:legacy_flag) # rubocop:disable Project/ForbidFlipperToggleInSpecs
When touching a file, prefer replacing suppressions with enabled? stubs.
Team expectations
Do not add new
Flipper.enable/disablecalls in specs.Use
enabled?stubs for new or modified tests.Keep suppression comments only for legacy code you are not refactoring yet.
Rollout plan
Enforce
Project/ForbidFlipperToggleInSpecsin CI.Keep existing inline suppressions for legacy callsites.
Require new or modified specs to use
enabled?stubs.Gradually remove suppressions as files are updated.
FAQ
Is this a behavior change to production feature flags?
No. This is test-only guidance and lint enforcement.
Can I disable this Rubocop in new code?
Please, no. New tests should use stubs. If you must add a suppression, explain why in the PR description.
Does Flipper use an in-memory adapter in test?
Flipper may default to memory in Rails test setups, but in vets-api we still standardize on stubs in specs to avoid direct global mutation and to keep test setup explicit and local to the example.
What if I need to control many flags?
Use one enabled? stub per flag, or a local helper that sets all needed stubs in one place.
What if I only care about one flag and want everything else untouched?
Use and_call_original first, then override only the feature you need:
before do
allow(Flipper).to receive(:enabled?).and_call_original
allow(Flipper).to receive(:enabled?).with(:feature_name).and_return(true)
end
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.