---
title: How to stub Feature Flags with RSpec
teaser: 'Are your feature flags states leaking between your test cases? Stub them
  all to make your test suite more reliable. Here''s how.

  '
tags: testing,rspec,rails,web
author:
- Stefanni Brasil
- Elias Rodrigues
published_on: 2023-02-03
---

If you are using feature flags with [Flipper] or a similar gem on your Rails project,
and storing them in Redis, you may run into the issue of leaking feature flag
states between test cases.

That increases the chances of making your test suite flaky,
and an easy strategy to counter that is to stub all of the feature flags on your specs.

[Flipper]: https://rubygems.org/gems/flipper

## Creating Feature Flag Helper Methods With RSpec

Inspired by the [strategy that GitLab follows] to stub all feature flags by default, we adapted it to our case, which was a much simpler scenario.

[strategy that GitLab follows]: https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/spec/support/helpers/stub_feature_flags.rb

The first step is to create a helper method in your RSpec support folder:

```rb
# spec/support/helpers/stub_feature_flags.rb

module StubFeatureFlags
  # Enable/disable Flipper globally
  # Disabled by default in spec/spec_helper.rb
  def stub_all_feature_flags(value)
    allow(Flipper).to receive(:enabled?).and_return(value)
  end

  # Unstub any global stubs
  # aka undo `stub_all_feature_flags`
  # More details at the end of the post
  def unstub_all_feature_flags
    RSpec::Mocks.space.proxy_for(Flipper).reset
  end

  # Enable/disable Flipper for a specific feature for any actors
  # e.g.: stub_feature_flag(:allow_cat_to_have_snacks, true)
  def stub_feature_flag(feature_name, value)
    allow(Flipper).to receive(:enabled?).with(feature_name, anything).and_return(value)
    allow(Flipper).to receive(:enabled?).with(feature_name).and_return(value)
  end

  # Enable/disable Flipper for a specific feature and actor
  # e.g.: stub_feature_flag_for_actor(:allow_cat_to_have_snacks, cat, true)
  def stub_feature_flag_for_actor(feature_name, actor, value)
    allow(Flipper).to receive(:enabled?).with(feature_name, actor).and_return(value)
  end
end
```

The next step is to make those stub methods available to the whole RSpec test suite:

```rb
# spec/spec_helper.rb

require "support/helpers/stub_feature_flags.rb"

RSpec.configure do |config|
  config.include StubFeatureFlags
  # ...

  # Disable all feature flags by default
  # Use StubFeatureFlags methods to stub feature flags
  config.before(:each) do
    stub_all_feature_flags(false)
  end
  # ...
end
```

Now, to test behaviors related to feature flags, we need to stub them explicitly on our test cases. Let's see how.

## How to use Feature Flag Helper Methods in RSpec test cases

Our `Cat` model has a feature behind a feature flag called `allow_cat_to_have_snacks`.

In our Cat Shop, only the cats with the feature flag enabled can have snacks, so we need to test
the business logic around their snack allowance.

Here are some test cases to show how to use the helper methods for our cats:

```rb
# spec/models/cat_spec.rb

describe Cat do
  describe "#can_have_snacks?" do
    context "when feature flag allow_cat_to_have_snacks is enabled for all cats" do
      it "returns true" do
        cat = create(:cat)
        stub_feature_flag(:allow_cat_to_have_snacks, true)

        expect(cat.can_have_snacks?).to be(true)
      end
    end

    context "when feature flag allow_cat_to_have_snacks is enabled only for specific cats" do
      it "returns true only for the allowed cats" do
        allowed_cat = create(:cat)
        not_allowed_cat = create(:cat)

        stub_feature_flag_for_actor(:allow_cat_to_have_snacks, allowed_cat, true)
        stub_feature_flag_for_actor(:allow_cat_to_have_snacks, not_allowed_cat, false)

        expect(allowed_cat.can_have_snacks?).to be(true)
        expect(not_allowed_cat.can_have_snacks?).to be(false)
      end
    end
  end
end
```

## Unstubbing Feature Flags For Flipper groups

There might be cases where resetting the default stubbing in the `spec/spec_helper.rb` is necessary.

In our app, we had a Flipper group defined in the gem's initializer:

```rb
# config/initializers/flipper.rb

Flipper.register(:cat_owners) do |actor|
  actor.respond_to?(:cats) && actor.cats.any?
end
```

A Flipper Group checks if a flag is `enabled?` for its actors behind the scenes. Because our spec helper sets Flipper's `enabled?` to always return `false`, we needed to undo that default stub.

Otherwise, the following test would fail because `enabled?` was always returning `false` for any actors:

```rb

# spec/models/user_spec.rb

describe User do
  describe "#can_pet_cat?" do
    it "returns true only for cat owners" do
      unstub_all_feature_flags # <-- resets Flipper stubs
      Flipper.enable_group(:allow_pet_cat, :cat_owners)

      user = create(:user)
      user_cat_owner = create(:user, cat_owner: true)

      expect(user.can_pet_cat?).to be(false)
      expect(user_cat_owner.can_pet_cat?).to be(true)
    end
  end
end
```

Calling `RSpec::Mocks.space.proxy_for(Flipper).reset` on our `StubFeatureFlags` will remove the stubs, therefore allowing the above test to succeed. Handy for undoing any stubs on your specs.

## Bye, Flaky Feature Flags Tests!

And that's it!

Now, because we are required to explicitly stub feature flags only on test cases that need them,
our test suite became more reliable and easier to maintain.

This approach also made it easier to test both scenarios: when the feature flag is enabled and when it isn't.

All of those benefits while not having flaky tests anymore!

Now our Cat Shop's employees can nap peacefully knowing the cat's snack allowance is properly tested 🐱.
