Topic: RSPec Mock going out of scope?

Have an interesting problem with an RSpec mock going out of scope:

# RSpec Question

class Node < ActiveRecord::Base

  belongs_to :language
  acts_as_nested_set :scope => :root_id
 
  def language_name
    self.root? ? language.name : parent.language_name
  end
end

describe Node, "instance" do

  fixtures :nodes

  before(:each) do
    @language = mock_model(Language, :name => "Japanese")
    @node = Node.create!(:language => @language)
    @section1 = Node.create!()
    @chapter1 = Node.create!()
  end

  it "should return it's own language if it is root" do  # Passes
    @language.should_receive(:name).exactly(:once).and_return("Japanese")
    @node.language_name.should == "Japanese"
  end
 
  it "should return it's parent's language if it is a child" do # Fails (message below)
    @section1.move_to_child_of(@node)
    @chapter1.move_to_child_of(@section1)
    @language.should_receive(:name).exactly(:once).and_return("Japanese")
    @section1.language_name.should == "Japanese"
    @language.should_receive(:name).exactly(:once).and_return("Japanese")
    @chapter1.language_name.should == "Japanese"
  end
end


The spec failure I get is:
 NoMethodError in 'Node instance should return it's parent's language if it is a child'
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.name
/Users/mikel/working/universal_translator/config/../app/models/node.rb:29:in `language_name'
/Users/mikel/working/universal_translator/config/../app/models/node.rb:29:in `language_name'
./spec/models/node_spec.rb:160:
script/spec:4:

On following the execution with a debugger, I find that it correctly calls language_name recursively up to the root node, then tries to get "language.name" and gets nil.  Looking at the parent.language field gives an ID of the mock language so that's not the problem.

Sort of seems like when the recursive call happens to self.language_name, it looses the scope of the Rspec mock @language, especially as the call to the root node in the first spec passes.

Any ideas?

Regards

Mikel

Last edited by Mikel Lindsaar (2007-07-18 08:28:39)

Re: RSPec Mock going out of scope?

I got some help from David Chelimsky on the RSpec mailing list.  this is what he had to say:

The parent method does a find, so it retrieves a new object from the db - not the instance of Node you have defined. This new instance has no language associated with it (because the one you gave it pretends to exist by sporting an ID, but is never saved to the DB).

He then gave me this example:

[code ruby]
require File.dirname(__FILE__) + '/../spec_helper'

describe Node, "instance" do

  before(:each) do
    @language = mock_model(Language, :name => "Japanese")
    @node = Node.new(:language => @language)
    @section1 = Node.new()
    @chapter1 = Node.new()
    @node.stub!(:root?).and_return(true)
    @section1.stub!(:root?).and_return(false)
    @chapter1.stub!(:root?).and_return(false)
  end

  it "should return it's own language if it is root" do  # Passes
    @node.language_name.should == "Japanese"
  end
 
  it "should return it's parent's language if it is a child" do # Fails (message below)
    @section1.stub!(:parent).and_return(@node)
    @chapter1.stub!(:parent).and_return(@section1)
    @section1.language_name.should == "Japanese"
    @chapter1.language_name.should == "Japanese"
  end
end
[/code]

That handles it fully.

Regards

Mikel

Last edited by Mikel Lindsaar (2007-07-19 01:36:41)