Forum: RSpec Weird message on ActiveSupport::Callbacks::CallbackChain:Class

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
161c0829c95d759f4dc0e0b71f712109?d=identicon&s=25 Rémi Gagnon (rgagnon)
on 2009-02-10 17:16
(Received via mailing list)
Hi guys,

its probably a miss understanding of the framework but I need your
assistance.

Here is the problem of one of my teammates:
I have a hard time writing rspecs for a class I developed. The class in
question reads an xml document, instanciates the model classes found in
the
xml and saves the classes and their attributes in the database with the
good
foreign keys. The class is stored in a lib.

The class :

class XmlReader

 def self.import_xml(xml_doc)
  objects_xml = Array.new
  keys_obj = Array.new
  xml = REXML::Document.new(xml_doc)
  read_xml(xml, objects_xml, keys_obj)
  raise AnError.new("Can not save xml objects") if (objects_xml.empty?)
  objects_xml.each { |instance|
    save_class(instance, keys_obj[objects_xml.index(instance)])
  }
 end
 …
end

The import_xml function acts as an entry point but there are two other
functions involved in the process :
-          read_xml : reads the xml elements one by one and instanciates
the
classes that have to be with the attributes found in the xml document.
It is
called recursively. The two arrays,  objects_xml and keys_obj, contain
respectively all the instanciated objects found and all the foreign keys
defined in the relations of the models.
-          save_class : saves the instanciated objects contained in the
objets_xml array with the good foreign keys for the relations to be well
done.

Now, I am trying to test if the objects_xml array is empty. If so, the
class
should raise an error.

The rspec :

# mocks …
@mock_element = mock(REXML::Element)
mock_element.stub!(:text)
@mock_element.stub!(:name)
@mock_element.stub!(:elements)
@xml_doc = mock(REXML::Document, :elements => { "e1" => @mock_element,
"e2"
=> @mock_element })
@xml_doc.stub!(:new)
@xml_doc.stub!(:has_elements?)
xml_doc.stub!(:encoding)
@objets_xml = mock(Array)
@objets_xml.stub!(:[]=)
@objets_xml.stub!(:[])
objets_xml.stub!(:empty?)
@keys_obj = mock(Array)
@keys_obj.stub!(:[]=)
@keys_obj.stub!(:[])

Array.should_receive(:new).and_return(@objets_xml)
Array.should_receive(:new).and_return(@keys_obj)
REXML::Document.should_receive(:new).and_return(@xml_doc)
XmlReader.should_receive(:read_xml).and_return(true)
objets_xml.should_receive(:empty?).and_return(true)
lambda { XmlReader.import_xml(xml_valid) }.should raise_error(AnError,
"Can
not save xml objects")

The error :

undefined method `new' for ActiveSupport::Callbacks::CallbackChain:Class

This is where my problem is. This error occurs after the test is made
and
finished. In other words, the test passes but the framework fails after
it
on a callback made in rails for whatever reason.

Stack trace :

C:/Projets/Iter8_Conversion/vendor/rails/actionpack/lib/action_controller/test_process.rb:471:in
`method_missing'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/mocks/proxy.rb:102:in
`__send__'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/mocks/proxy.rb:102:in
`message_received'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/mocks/proxy.rb:140:in
`new'
(eval):7:in `teardown_callback_chain'
C:/Projets/Iter8_Conversion/vendor/rails/activesupport/lib/active_support/callbacks.rb:277:in
`send'
C:/Projets/Iter8_Conversion/vendor/rails/activesupport/lib/active_support/callbacks.rb:277:in
`run_callbacks'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-rails-1.1.12/lib/spec/rails/interop/testcase.rb:10
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_methods.rb:84:in
`instance_eval'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_methods.rb:84:in
`eval_each_fail_slow'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_methods.rb:82:in
`each'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_methods.rb:82:in
`eval_each_fail_slow'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_group_methods.rb:254:in
`run_after_each'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_group_methods.rb:253:in
`each'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_group_methods.rb:253:in
`run_after_each'
C:/ruby/lib/ruby/gems/1.8/gems/rspec-1.1.12/lib/spec/example/example_methods.rb:141:in
`after_each_example'

Can anyone explain what is happening here or does anyone knows how to
bypass
this ?

Many thanks in advance.
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-02-11 06:33
(Received via mailing list)
On Tue, Feb 10, 2009 at 9:03 AM, Remi Gagnon <rem.gagnon@gmail.com>
wrote:
>
>   objects_xml.each { |instance|
> called recursively. The two arrays,  objects_xml and keys_obj, contain
>
> @objets_xml = mock(Array)
> XmlReader.should_receive(:read_xml).and_return(true)
> objets_xml.should_receive(:empty?).and_return(true)
> lambda { XmlReader.import_xml(xml_valid) }.should raise_error(AnError, "Can
> not save xml objects")

This is one of those "Doctor, it hurts when I do this" sorta
situations. The whole point of TDD, and BDD, is to write code examples
first so that you end up with code that is inherently testable.
Back-filling specs on legacy code (code without tests!) is painful,
and you are suffering that pain here.

That said, what I'd do here is take a cue from Michael Feather's
Working Effectively with Legacy Code. Start with a high level
characterization test in which you pass the appropriately structured
xml doc and specify the expected outcomes. Don't worry about granular
specs about which step in the procedure is doing what at first. Once
you have a few of those in place, start to refactor those bits out to
other methods that you can spec directly.

I realize that this doesn't solve your specific problem directly, but
I really have no clue from where I'm sitting as to where
ActiveSupport::Callbacks::CallbackChain:Class is injecting itself into
this process.

HTH,
David
161c0829c95d759f4dc0e0b71f712109?d=identicon&s=25 Rémi Gagnon (rgagnon)
on 2009-02-12 03:17
(Received via mailing list)
Thank you David,

You are right we are dealing with some code that need to be refactored.

We will dig deeper to have more info about this Callbackchain problem

Rémi
This topic is locked and can not be replied to.