DEV Community

Kyle Chilcutt
Kyle Chilcutt

Posted on • Originally published at blog.chilcutt.com on

RSpec nested subjects

The RSpec subject functionality can be useful for simplifying your spec setup and getting straight to the point. I really like using an explicit subject because it helps describe what functionality is under test when you’re writing your specs. Recently, I learned it was possible to nest your subject when using a named subject.

programming

Photo by Clément H on Unsplash

The subject syntax allows you to use the one-liner syntax for writing a test which can really clean up a spec file. Let’s take a trivial example of a Rails model:

describe Car do
  let(:car) { Car.new }

  it "is a vehicle" do
    expect(car).to be_a(Vehicle)
  end
end

For this trivial case, you’ll notice that our expectation pretty literally matches the description of the test case. This might be a good use case for subject:

describe Car do
  subject { Car.new }

  it { is_expected.to be_a(Vehicle) }
end

The above will test the same functionality and also leave pretty similar spec output when the spec is run.

failures


Failures look slightly different, but make the same point.

You can also redefine your subject for different blocks of your test. Let’s set the subject for an instance method that I want to test:

describe Car do
  subject { Car.new }

  it { is_expected.to be_a(Vehicle) }

  describe "#parked?" do
    subject { Car.new.parked? }

    it { is_expected.to eq(false) }
  end
end

Dope. By redefining the subject we can use the one-line syntax wherever we want.

The final trick is we can add an name to our initial subject so we can reference it later within the spec:

describe Car do
  subject(:car) { Car.new }

  it { is_expected.to be_a(Vehicle) }

  describe "#parked?" do
    subject { car.parked? }

    it { is_expected.to eq(false) }
  end
end

I find this particularly useful if the subject in the enclosing block has some complex setup:

describe Car do
  let(:driver) { Person.new }
  let(:front_seat) { Person.new }
  subject(:car) { Car.new(driver: driver, front_seat: front_seat) }

  it { is_expected.to be_a(Vehicle) }

  describe "#parked?" do
    subject { car.parked? }

    it { is_expected.to eq(false) }
  end
end

Now we can leverage any earlier setup while still having the benefits of different subjects in each context.


Shout out to Zack Mariscal for helping me edit and improve this post and my writing in general.

Oldest comments (0)