1

I'm currently taking Coursera's free Ruby on Rails Introduction class. I'm working on the third assignment which contains creating a People class where you have some functionality like a search function.

I'm getting a weird error when I run rspec with their designed unit tests. I'm 99% sure the error is lying in the unit tests. Specifically, before I've even touched any files, I'm getting the following error:

        raise <<-EOS
        #{description} accessed in #{article} #{hook_expression} hook at:
          #{CallerFilter.first_non_rspec_line}

        `let` and `subject` declarations are not intended to be called
        in #{article} #{hook_expression} hook, as they exist to define state that
        is reset between each example, while #{hook_expression} exists to
        #{hook_intention}.
        EOS

      RuntimeError:
        let declaration `class` accessed in an `after(:context)` hook at:
          /Users/<username>/.rvm/gems/ruby-2.4.0/gems/rspec-core-3.7.1/exe/rspec:4:in `<top (required)>'

        `let` and `subject` declarations are not intended to be called
        in an `after(:context)` hook, as they exist to define state that
        is reset between each example, while `after(:context)` exists to
        cleanup state that is shared across examples in an example group.

For starters, I don't totally understand the syntax they're using to describe talking about their tests. Secondly, here is the raw testing file that the author's of the Coursera class wrote:

require 'rspec'
require 'rspec/its'
require_relative '../module2_lesson3_formative.rb'

describe "lesson3" do

  context "check results" do
    p1 = Person.new("Ivana", "Trump")
    p2 = Person.new("Eric", "Trump")
    p3 = Person.new("Melania", "Trump")
    p4 = Person.new("Marla", "Maples")

    it "unexpected search result" do
      expect(Person.search("Trump").size).to be == 3
    end
  end

  context "check instance properties" do
    subject(:john) { Person.new("Chris", "Christie") }

    it "missing first_name" do
      is_expected.to respond_to(:first_name)
    end

    it "missing last_name" do
      is_expected.to respond_to(:last_name)
    end

  end

  context "check class properties" do
    subject(:class) { Person }

    it "missing search" do
      is_expected.to respond_to(:search)
    end
  end
end

I am hoping that someone can explain to me the debugging information when I run rspec. I'm using RSpec 3.7 which I'm guessing is the problem, as indicated that it might be a versioning upgrade thing here. That would also explain the fact that the class's authors didn't intentionally push up bad code. What is the best way for me to fix this and why are lines like this:

subject(:john) { Person.new("Chris", "Christie") }

in bad form? Thanks so much! Really appreciate your time :)

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
jlarks32
  • 931
  • 8
  • 20
  • Are you really using Rails 3? – Sebastián Palma Jan 13 '18 at 16:57
  • @SebastianPalma No, `ruby -v` returns `ruby 2.2.3p173`. `rails -v` returns `Rails 4.2.3` and `rspec -v` returns `RSpec 3.7`. Sorry if that wasn't clear - I thought the issue might be versioning with rspec. – jlarks32 Jan 13 '18 at 17:00
  • Maybe the problem is in the `subject(:class) { Person }` line? What's intended by the author to achieve?, isn't `subject { Person.new }` enough to set the subject to an instance of Person and get this class? – Sebastián Palma Jan 13 '18 at 17:14
  • @SebastianPalma That's a great question. I don't really have an idea. But I found this site: http://blog.teamtreehouse.com/an-introduction-to-rspec and modified it to the following. I'll add it in a response, because it's too long. You should post a response tho so I can confirm your answer and +1 you. – jlarks32 Jan 13 '18 at 17:21
  • @jlarks32 - why didn't you PR this? :) – Kalman Oct 14 '18 at 18:19
  • @Kalman haha what do you mean? – jlarks32 Oct 18 '18 at 15:36

2 Answers2

1

This seems to have resolved the problem that rspec was complaining about:

describe "lesson3" do

  subject { person } # ADDED THIS LINE
  context "check results" do
    p1 = Person.new("Ivana", "Trump")
    p2 = Person.new("Eric", "Trump")
    p3 = Person.new("Melania", "Trump")
    p4 = Person.new("Marla", "Maples")

    it "unexpected search result" do
      expect(Person.search("Trump").size).to be == 3
    end 
  end

  context "check instance properties" do
    let(:person) { Person.new("Chris", "Christie") } # CHANGED THIS LINE

    it "missing first_name" do
      is_expected.to respond_to(:first_name) 
    end 

    it "missing last_name" do
      is_expected.to respond_to(:last_name) 
    end 

  end

  context "check class properties" do
    let(:person) { Person } # CHANGED THIS LINE

    it "missing search" do
      is_expected.to respond_to(:search) 
    end 
  end
end

I still am not sure why, or what the difference is implying. I would love someone to explain it a little bit more in depth.

jlarks32
  • 931
  • 8
  • 20
  • I guess you mean `Person` instead `person`, a local variable defined nowhere. With the use use of `let(:person)` in both cases you're doing anything, so, they could be omitted. `is_expected` takes the subject already defined. – Sebastián Palma Jan 13 '18 at 17:30
  • I'm not totally sure I follow. I declared `subject {person}` at the top (it'd be line no 3 if there were numbers). Are `subject` and `let` just doing the same thing? – jlarks32 Jan 13 '18 at 17:31
  • Well, if you set subject on 3rd line, you already don't need the `let(:person)`, because you're not using that person object. Or I guess what you mean is you declared `person` by using let and then the `subject` equal to that person declared before, so there's one step that could be reduced, or not? – Sebastián Palma Jan 13 '18 at 17:42
  • 1
    @SebastianPalma AH got it! my man. yeah that totally makes sense thanks for the help. I was essentially spreading out what could have been simplified to just `subject { Person}` or `subject {Person.new()}` got it. – jlarks32 Jan 13 '18 at 17:46
1

In order to change the subject class for your specs, then you can "redefine" subject per each of your examples, or as it's needed.

When trying to change the class of the spec subject, with let or subject, then you get the detailed error (warning) message:

let and subject declarations are not intended to be called in an after(:context) hook, as they exist to define state that is reset between each example

So you can't set explicitly the class of your subject, because it'll be resetted with each running example.

You can set your subject to be a Person object within the "check class properties" context by using just subject, this way is_expected will check in this object that responds to the class method search, like:

context "check class properties" do
  subject { Person }

  it 'missing search' do
    is_expected.to respond_to(:search)
  end
end
Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59