2
votes

I have a model spec that is failing with "undefined method 'save' for nil:NilClass'." This occurs in the class method 'create_and_send_self_eval'. The method is creating a new Evaluation, but it always returns nil in the test environment. I've also tried using 'create', 'create!' and they also return nil. However, this only occurs in the test environment. In the development environment, it returns the correct object. I'm using rspec 3.1.5, rails 4.1.6, and ruby 2.1.2.

I've included the code for the class and my debug output. Any suggestions?

Evaluation.rb

class Evaluation < ActiveRecord::Base
  has_one :evaluator
  validates_uniqueness_of :access_key
  validates_presence_of :participant_id

  before_validation :set_access_key, on: :create

  def send_invite
    return true
  end

  def self.create_and_send_self_eval(participant)
    evaluation = self.new do |e|
      e.participant_id = participant.id
      e.evaluator = participant
    end
    if evaluation.nil?
      binding.pry
    end
    evaluation.save
  end

  private

    def set_access_key
      return if access_key.present?

      begin
        self.access_key = SecureRandom.hex(8)
      end while self.class.exists?(access_key: self.access_key)
    end

end

Debug output using pry in the test environment

[1] pry(Evaluation)> participant
=> #<Participant id: 167, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-07 19:43:47", updated_at: "2014-10-07 19:43:47">
[2] pry(Evaluation)> Evaluation.new
=> nil
[3] pry(Evaluation)> Evaluation.create(participant_id: participant.id)
NoMethodError: undefined method `save' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/persistence.rb:34:in `create'
[4] pry(Evaluation)> Evaluation.create!(participant_id: participant.id)
NoMethodError: undefined method `save!' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/validations.rb:41:in `create!'

Debug output in rails console

2.1.2 :005 > p = Participant.last
  SQL (0.9ms)  SELECT  "participants"."id" AS t0_r0, "participants"."first_name" AS t0_r1, "participants"."last_name" AS t0_r2, "participants"."evaluation_url" AS t0_r3, "participants"."created_at" AS t0_r4, "participants"."updated_at" AS t0_r5, "evaluators"."id" AS t1_r0, "evaluators"."email" AS t1_r1, "evaluators"."created_at" AS t1_r2, "evaluators"."updated_at" AS t1_r3, "evaluators"."actable_id" AS t1_r4, "evaluators"."actable_type" AS t1_r5, "evaluators"."evaluation_id" AS t1_r6 FROM "participants" LEFT OUTER JOIN "evaluators" ON "evaluators"."actable_id" = "participants"."id" AND "evaluators"."actable_type" = 'Participant'  ORDER BY "participants"."id" DESC LIMIT 1
 => #<Participant id: 3, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-06 06:32:40", updated_at: "2014-10-06 06:32:40"> 
2.1.2 :006 > Evaluation.new
 => #<Evaluation id: nil, participant_id: nil, access_key: nil, created_at: nil, updated_at: nil> 
2.1.2 :007 > Evaluation.create(participant_id: p.id)
   (0.2ms)  BEGIN
  Evaluation Exists (2.1ms)  SELECT  1 AS one FROM "evaluations"  WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
  Evaluation Exists (0.3ms)  SELECT  1 AS one FROM "evaluations"  WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
  SQL (1.7ms)  INSERT INTO "evaluations" ("access_key", "created_at", "participant_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["access_key", "c688b05ee4625c60"], ["created_at", "2014-10-07 19:47:15.877706"], ["participant_id", 3], ["updated_at", "2014-10-07 19:47:15.877706"]]
   (2.3ms)  COMMIT
 => #<Evaluation id: 4, participant_id: 3, access_key: "c688b05ee4625c60", created_at: "2014-10-07 19:47:15", updated_at: "2014-10-07 19:47:15">

pry debug output at beginning of method

[1] pry(Evaluation)> self
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[2] pry(Evaluation)> self.class
=> Class
[3] pry(Evaluation)> self.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0

[4] pry(Evaluation)> Evaluation
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[5] pry(Evaluation)> Evaluation.class
=> Class
[6] pry(Evaluation)> Evaluation.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0

I didn't show the entire output for self.connection or Evaluation.connection. But connection is returning correctly.

2

2 Answers

2
votes

The answer to this question had to do with how the tests were written. In my spec, I'm checking to see if "new" is called on Evaluation. Since, I'm using rspec-mocks, Evaluation is not actually being created. Fixed this by changing the test to test the output results.

0
votes

Try doing this instead:

evaluation = self.new.tap do |e|
  e.participant_id = participant.id
  e.evaluator = participant
end

Using Object#tap should guarantee that you set evaluation to the object rather than to the return value of the block.