Tips, tricks and how to refactor your Rspec

As a Rails developer, I think most of you already have a chance working with Rspec. Sometimes you felt your code so hard to read and understandable but you don't know how to improve it. Even you already tried to reading some documents like [BetterSpec](http://www.betterspecs.org/) or following [Rspec Style Guide](https://github.com/rubocop-hq/rspec-style-guide). Have you ever wondered if you can make the RSpec more readable? 

![view-ape-thinking-primate-33535.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542868/62875f09-dc8d-05ed-23c4-e1a31073f978.jpeg)
*My feeling when I try to read my code*

In this post, I will share my way to improve your RSpec from my experiment including from another author I collected here. Please comment below and together we can have better guidelines for anyone need.

This post will be separated into 3 main topics:
- Structure of RSpec
- How to test your Model
- How to refactor your RSpec

But before we can go to the detail, we need to have a basic understanding of RSpec, so the easiest way is to answer the question about **What is the Structure of RSpec?**. You can check it [here]()
# Structure of RSpec

## Overview

In this part, I will try to clear some Misunderstood and unclear things: 

- Describe vs Context
- Before vs Let vs Subject
- Before & Context / Subject & Describe


## Describe / Context

The block below is showing how often people using both **describe** and **context** :

```ruby
describe Ranking, type: :model do
  describe "ActiveModel scope" do
    describe '#available' do
      context 'valid target record is existed' do
      #test
      end
    end
  end

  describe '#notify_user' do
    context 'user allows notifying' do
      #test
    end
  end
end
```

- According to the [RSpec source code](https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/example_group.rb), **context** is just an alias method of **describe** -> No difference between these two methods
- Using both **describe** blocks and **context** blocks together
- **Describe** is to wrap a set of tests against one functionality -> For **things**
- **Context** is to wrap a set of tests against one functionality under the same state -> For **state**

```ruby
describe Ranking, type: :model do
  let(:user)    { create(:profile, :male).user }
  let(:ranking) { user.ranking }

  describe "ActiveModel scope" do
    describe '#available' do
      before { 100.times { create(:user, last_active_at: rand(20.days).seconds.ago) } }
      context 'valid target record is existed' do
        before do
          correct_ranking
        end

        it { expect(Ranking.available.count).to eq 100 }
      end

      context 'invalid target record has existed' do
        it { expect(Ranking.available.count).to eq 0 }
      end
    end
  end

  describe "#public instance methods" do
    describe '#notify_user' do
      context 'user allows notifying' do
        before do
          ranking.update!
          ranking.notify_user
        end

        it { expect(PushNotifications::RankingLevelJob).to have_been_enqueued }
      end

      context 'user account has been rejected' do
        let(:user) { create(:user, status: :rejected) }
        before { user.ranking.notify_user }

        it { expect(PushNotifications::RankingLevelJob).to_not have_been_enqueued }

      end
    end
  end
end
```

#### Things

**Bad:**

```ruby
context '#calculator'
```

**Good:**

```ruby
describe '#calculator'
```


#### States

**Bad:**

```ruby
describe 'when the number is negative'
```
**Good:**

```ruby
context 'when the number is negative'
```

### Note:

- **context** isn't available at the top-level
- Organize your tests by things

#### Prefer to use `context` in:
- Tests usually deal with permutations of state.
- Nested state so using nested **context** is the better choice
- Use **describe** when we have complex return values.

#### `context` naming:
- Should not match your method names!
- Explain *why* we would call the method

**Bad**

```ruby
describe "#assign"
```

**Good**

```ruby
context "assigning a new value for the number A"
```


## Before / Let / Subject

### Before vs Let vs Let!

- **let** block is only executed when referenced, lazy loading. This means that `let()` is not evaluated until the method that is formed is run for the first time -> only run once.

- **let!** blocks are executed in the order they are defined (much like a before the block). The core difference between **let!** and **before** is that you get an explicit reference to this variable, rather than needing to fall back to instance variables. 

- **let!** can run multiple time

- **before** eagerly runs code before each example, even if the example does not use any of instance variables defined in the blocks 

- Use **before** for actions
- Use **let** or **let!** for dependencies (real or test double)


#### Actions

**Bad:**

```ruby
let(:dummy) do
  @calculator.initialize_number
end
```

**Better:**

```ruby
before do
  @calculator.initialize_number
end

#or 

before { @calculator.initialize_number }
```

#### Dependencies

**Bad:**

```ruby
before { @multiple_levels = [1, 3, 5] }
```

**Better:**

```ruby
let(:multiple_levels) { [1, 3, 5] }
```


### Before vs Subject

### Let vs Subject

- `let` defines a named variable
- `subject` is the object being tested
    - is automatically based on the `describe`.
    - can be explicitly specified.
    - can have a name.
    - will be the target of "bare" `should`.

- Both methods are lazy loading.
- Can use `subject!` to instantiated eagerly (before an example in its group runs)
- Can expect without writing `subject` or the name of a named subject
- Use `subject` for the thing you are testing
- Use `let` for đependencies

**Bad:**

```ruby
let(:book) { Book.new(name: "The Hobbit", author: "J. R. R. Tolkien") }
```

**Better:**

```ruby
subject(:book) { Book.new(name: "The Hobbit", author: "J. R. R. Tolkien") }
```


## Before & Context / Subject & Describe

### Before & Context

- In the official documentation don't have any mention about the relative about before & context, but when trying to understand the purpose of each method. We can see `before` aligns with the `context`
    - `context` is used for the state.
    - `before` lists the actions to get to that state.

```ruby
context "the number of books can be borrowed has been maximized"
  before do
    10.times { book_basket << Book.new }
  end
end
```


### Subject & Describe

- Like the `before & context`, the relation between `subject & describe` is the same. `subject` aligns with the `describe`
    - `describe` is used for things
    - `subject` specifies the thing to test

**At the top-level** 

```ruby
describe Calculator do
  subject(:cal_target) { Calculator.new(a: 5, b: 10) }
end
```

**Can be used as nested level**

```ruby
describe Calculator do
  subject(:cal_target) { Calculator.new(a: 5, b: 10) }

  describe 'the result after calculate' do
    subject(:cal_result) { cal_target.calculate }
  end
end
```

*To be continued in the next part*