Proper usage of subject

10 views
Skip to first unread message

Alexander Popov

unread,
Nov 28, 2018, 11:38:34 AM11/28/18
to rspec
Hello,

Given a class:

class Person
 
def name
   
'John'
 
end

 
def age
   
20
 
end
end


what is the proper way to use subject?

Version 1:

RSpec.describe Person do
  subject
(:person) { Person.new }

  it
'#name' do
    expect
(person.name).to eq 'John'
 
end

  it
'#age' do
    expect
(person.age).to eq 20
 
end
end

Version 2:

RSpec.describe Person do
  let
(:person) { Person.new }

  describe
'#name' do
    subject
{ person.name }

    it
{ is_expected.to eq 'John' }
 
end

  describe
'#age' do
    subject
{ person.age }

    it
{ is_expected.to eq 20 }
 
end
end

I think that most people have the very deeply-rooted belief that subject necessarily needs to hold an instance of the class under test (Version 1). This might partly come from this passage in the documentation:

https://relishapp.com/rspec/rspec-core/v/3-8/docs/subject/implicitly-defined-subject

> If the first argument to the outermost example group is a class, an instance of that class is exposed to each example via the subject method.

However, I think that here the documentation simply describes what the default behavior is and doesn't imply that it always has to be that way. My understanding is that subject conceptually represents the thing under testing. If we want to assert the result of a method call, than subject better hold this result. For example, again given a class:

class Person
  attr_accessor
:name

 
def initialize(name)
   
@name = name
 
end

 
def greeting
   
"Hi, #{name}!"
 
end
end

I find it more appropriate to use subject like so:

RSpec.describe Person do
  describe
'#greeting' do
    context
'when the person is called John' do
      subject
{ Person.new('John').greeting }

      it
{ is_expected.to eq 'Hi, John!' }
   
end

    context
'when the person is called Merry' do
      subject
{ Person.new('Merry').greeting }

      it
{ is_expected.to eq 'Hi, Merry!' }
   
end
 
end
end

instead of:

RSpec.describe Person do
  subject
(:person) { Person.new(name) }

  describe
'#greeting' do
    context
'when the person is called John' do
      let
(:name) { 'John' }

      it
{ expect(subject.greeting).to eq 'Hi, John!' }
   
end

    context
'when the person is called Merry' do
      let
(:name) { 'Merry' }

      it
{ expect(subject.greeting).to eq 'Hi, Merry!' }
   
end
 
end
end

I'd like to know what is the recommended way of using subject. Thank you very much.

Jon Rowe

unread,
Nov 28, 2018, 11:44:01 AM11/28/18
to rs...@googlegroups.com
Hello

A `subject` is just a special instance of `let`.

It is intended to give attention to the “subject” under test, originally it had some special powers as you note (such as the implicit subject, and chaining matchers of that as `is_expected.to matcher`) but those remain as syntactic sugar only really.

We advocate that you at least name your subject (as you have), `subject(:person)` and then use it either implicitly (as `is_expected.to` or explicitly as its name, e.g. `expect(person).to`. 

Its value really depends on how you use it, as it is shared between various tests in a context, it should be the common value for that context.

Personally I just use a let.

Hope that helps.
Jon Rowe
---------------------------
Reply all
Reply to author
Forward
0 new messages