Cameron Bass Cameron Bass - 7 months ago 13
Ruby Question

Rspec - Trying to understand nil:NilClass error

I'm trying to make a test pass for code that has already has been implemented. I'm new to unit testing and RSpec and for the life of me cannot figure out this error. Basically, I'm trying to verify that JSON is being sent to the proper endpoint. I think I have created all the attributes I needed to but I keep getting this same error. Here is the error and my code. it's clearly something to do with including the city from another model table but I'm not sure why it's complaining?

Here is the test.

require "rails_helper"

RSpec.describe "/api/retailers" do

describe "GET /api/retailers" do
it "Returns JSON for retailers" do

location = Location.create!(
city: "Portland",
street_1: "Cherry",
state: "Oregon",
zip: "49490"
)

retailer = Retailer.create!(
name: "Good Coffee Co.",
description: "Hipster Good",
image_url: "http://www.example.com/foo_bar.jpg",
)

get "/api/retailers.json"

expect(response).to be_success
json = JSON.parse(response.body)

expect(json["name"]).to eql("Good Coffee Co.")
expect(json["description"]).to eql("Hipster Good")
expect(json["image_url"]).to eql("http://www.example.com/foo_bar.jpg")
expect(json["city"]).to eql(location.city)
end
end
end


Here is my error message

/api/retailers GET /api/retailers Returns JSON for retailers
Failure/Error: location.city

NoMethodError:
undefined method `city' for nil:NilClass
# ./app/models/retailer.rb:8:in `city'


Here is my controller

class Api::RetailersController < ApiController
def index
@retailers = Retailer.all
render json: @retailers, methods: [:city]
end
end


Here is my model

class Retailer < ActiveRecord::Base
has_one :location

has_many :retailer_timeslots
has_many :timeslots, through: :retailer_timeslots

def city
location.city
end
end


Any help would be awesome. I'm really stumped on this problem. Let me know if any other info is necessary

Answer

Your Retailer model has a relationship with location.

And you have a city method on it as well, which is looking at this location

def city
  location.city
end

Now, this is included in your controller:

render json: @retailers, methods: [:city]

Now, in your test, you created a location and a retailer:

location = Location.create!(
      city: "Portland",
      street_1: "Cherry",
      state: "Oregon",
      zip: "49490"
    )

  retailer = Retailer.create!(
    name: "Good Coffee Co.",
    description: "Hipster Good",
    image_url: "http://www.example.com/foo_bar.jpg",
  )

But these two are not related in any way.

So, when in your test, you send a get request to the index action of your controller,

get "/api/retailers.json"

This tries to get the city for the retailer, which is the method above. but remember that the method tries to get location.city

But your retailer has no location, because it has not been linked to any... So, location here will be nil

and then, when you call city on location(which is nil), you are calling city on nil

This is where the error comes from:

NoMethodError:
   undefined method `city' for nil:NilClass
 # ./app/models/retailer.rb:8:in `city'

Edit (To Fix):

Add the location created as the Retailers location in your test as follow:

location = Location.create!(
  city: "Portland",
  street_1: "Cherry",
  state: "Oregon",
  zip: "49490"
)

retailer = Retailer.create!(
  name: "Good Coffee Co.",
  description: "Hipster Good",
  image_url: "http://www.example.com/foo_bar.jpg",
  location: location # Add location as location here.
)
Comments