How to minimize RSpec `describe`/`context` nesting
Tags: ruby, rspec, gherkin
Published: 2019-01-18 04:32:59

---
title: How to minimize RSpec `describe`/`context` nesting
published: true
description: 
tags: Ruby,RSpec,Gherkin
---


## Deeply nested specs

Did you ever write the deeply nested code in RSpec?
I wrote a lot honestly.

In the application code, I avoid to write the deeply nested code. It's hard to read and hard to change, because of bumpy indentation.
When my collegue do code review, they pointing out the deeply nested code.
But in the RSpec, sometime (always) I wrote the deeply nested code and that passed the code review.

For example, like below:

```ruby
RSpec.describe "Screencast player", type: :system do
  context "When user logged in" do
    let(:user) { create(:user) }

    before do
      login_as(user)
    end

    context "When user register the credit card" do
      let(:card) { create(:card) }

      before do
        user.register_credit_card(card)
      end

      context "When there are screencast" do
        before do
          create(:screencast)
        end

        it "can play the screencast"
      end

      context "When theare not any screencast" do
        it "shows the message, 'no such screencast'"
      end
    end

    context "When user does not register the credit card" do
      it "can not play the screencast"
    end
  end

  context "When user is not logged in" do
    it "can not play the screencast"
  end
end
```

## Why it happened?

RSpec is awesome, it can be nest `describe`/`context` forever if you want.
Anything that can go wrong will go wrong. [^murphy law]
[^murphy law]: https://en.wikipedia.org/wiki/Murphy%27s_law

By the way I have the acceptance test written in Gherkin, how it would go?
It's never nested because it doesn't have the syntax for nesting.
https://docs.cucumber.io/gherkin/reference/

## How to fix it?

May the Gherkin be with you.

If you writing the specs like Gherkin, the specs behaves like Gherkin.
It never and ever nested, yay!

## How to do it?

I use the above example to explain how to do it.

First move arranging preconditions into the `shared_context`.

```ruby
RSpec.describe "Screencast player", type: :system do
  shared_context "When user logged in" do
    let(:user) { create(:user) }

    before do
      login_as(user)
    end
  end

  shared_context "When user register the credit card" do
    let(:card) { create(:card) }

    before do
      user.register_credit_card(card)
    end
  end

  shared_context "When there are screencast" do
    before do
      create(:screencast)
    end
  end
end
```

Then combine them

```ruby
RSpec.describe "Screencast player", type: :system do
  shared_context "When user logged in" do
    let(:user) { create(:user) }

    before do
      login_as(user)
    end
  end

  shared_context "When user register the credit card" do
    let(:card) { create(:card) }

    before do
      user.register_credit_card(card)
    end
  end

  shared_context "When there are screencast" do
    before do
      create(:screencast)
    end
  end

  describe "user play the screencast" do
    include_context "When user logged in"
    include_context "When user register the credit card"
    include_context "When there are screencast"

    it "can play the screencast"
  end

  describe "user knows there are no screencast yet" do
    include_context "When user logged in"
    include_context "When user register the credit card"

    it "shows the message, 'no such screencast'"
  end

  describe "user can not play the screencast before register the credit card" do
    include_context "When user logged in"

    it "can not play the screencast"
  end

  describe "user can not play the screencast before logged in" do
    it "can not play the screencast"
  end
end
```

That's it!

## Conclusion

Thank `shared_context` for fixing my deeply nested specs.

And Gherkin is awesome <3