The well-structured test suite helps check the necessary cases are covered at a glance. Developers looking into the code, later on, can quickly grasp which case they should add or modify. The famous unit testing framework provides us a way to organize the test cases in that manner.

RSpec is a de facto standard testing framework used in many Ruby projects. Although I have used RSpec in some projects, I did not fully understand how to describe, context, and it keyword correctly. These keywords are used just for representing the meaningless nested structure in my case. But that does not sound nice. Using these keywords properly leads us to inject an understandable form to the unit test written in RSpec. This article summarizes what we should think in writing RSpec test cases in terms of describe, context, and it use.

describe: Target Object

Let’s assume we have the following FizzBuzz class to be tested.

class FizzBuzz
  def self.run(n)
    if n % 3 == 0 && n % 5 == 0
      "FizzBuzz"
    elsif n % 3 == 0
      "Fizz"
    elsif n % 5 == 0
      "Buzz"
    else
      n
    end
  end
end

We want to ensure that FizzBuzz works as expected with RSpec. The target object is an instance of FizzBuzz.

describe FizzBuzz do
  # Test cases
end

context: Precondition

context is a place to hold the condition that should be satisfied before running the test. It can be a type of input or precondition imposed on the target class. We put the type of input passed to the run method of FizzBuzz.

describe FizzBuzz do
  context '3-multiple' do
    # Test here
  end

  context '5-multiple' do
    # Test here
  end

  context '15-multiple' do
    # Test here
  end

  context 'other' do
    # Test here
  end
end

it: Expectation

We describe the expected output from the method or object in it (or example).

describe FizzBuzz do
  context '3-multiple' do
    it 'Get Fuzz' do
      expect(FuzzBuzz.run(3)).to eq('Fuzz')
      expect(FuzzBuzz.run(6)).to eq('Fuzz')
    end
  end

  context '5-multiple' do
    it 'Get Buzz' do
      expect(FuzzBuzz.run(5)).to eq('Buzz')
      expect(FuzzBuzz.run(10)).to eq('Buzz')
    end
  end

  context '15-multiple' do
    it 'Get FizzBuzz' do
      expect(FuzzBuzz.run(15)).to eq('FizzBuzz')
      expect(FuzzBuzz.run(30)).to eq('FizzBuzz')
    end
  end

  context 'other' do
    it 'Get original number' do
      expect(FuzzBuzz.run(4)).to eq(4)
      expect(FuzzBuzz.run(8)).to eq(8)
    end
  end
end

This guideline is so helpful to me for writing the well-structured test in RSpec. The background information behind the scene is explicit with this structure.