On 17 May 2008, at 18:34, Jens Carroll wrote:
Hi All,

I am new to rspec and it seems that I don't understand some basics.

Hi Jens,

Jarkko answered your real question but there are other things worth pointing out, more about style than actually making the specs work.


---- spec -------

module XmlImportSpecHelper

 def mock_xml_import
   xml_file = RAILS_ROOT + "/spec/fixtures/import-member.xml"
   xml = File.read(xml_file)

   @xml_import = XmlImport.new
   @xml_import.should_receive(:open).exactly(1).times.
     with("any-file-name.xml").
     and_return(xml)
 end

end

You shouldn't have (hoho) a should_receive here. You're using the mock_xml_import in the before step, which is intended to set up mocks and stubs shared across the examples ("it" blocks). The code "@xml_import.should_receive(:open)..." should have its own example.

Also, since you're creating a *real* XmlImport, it doesn't make sense to call it "mock_xml_import", maybe "new_xml_import" would be clearer.


describe XmlImport do

 include XmlImportSpecHelper

 before(:each) do
   mock_xml_import
   @xml_import.parse_xml("any-file-name.xml")

   @country = mock_model(Country)
   Country.stub!(:find_by_short).and_return(@country)
 end

 it "should find country object for DE" do
Country.should_receive(:find_by_short).with("DE").and_return(@country)
   @xml_import.user.country.should equal(@country)
 end
end

Be clear what you are specifying here. Your call to #parse_xml is in the before block, which means you can't set expectations on it. I like to nest my methods in their own inner describe blocks, eg:

  describe XmlImport do
    before(:each) do
      @xml_import = XmlImport.new
    end

    describe "#parse_xml" do
      it "should open the XML file" do
        # ...
      end

      it "should find country object for DE" do
        # ...
      end
    end
  end

Be aware also that while "xml_import.should_receive(:open)" may work, it's a bit of a quirk of Ruby. The real method is define on the Kernel module, which is included into Object (so you get access to it everywhere). Since you know you're dealing with files, I'd use File.open, and specify that File.should_receive(:open) instead. Otherwise you may start thinking it's ok to specify interpendencies between methods in the class being specified.

Finally, this line:

  @xml_import.user.country.should equal(@country)


is known as a trainwreck- that is, multiple method calls strung together. It's usually a sign that something is modelled wrong. In this case, it's because your XmlParser class is doing too much.

This section

  (@doc/:member).each do |data|
    retrieve_and_save_user(data)
  end


Is the culprit. The XmlParser should parse XML, not retrieve and save user data (I know this, because it's called XmlParser, not UserDataRetrieverAndSaver, basically.) A more OO approach would be to make User responsible for this:

  (@doc/:member).each do |data|
    User.update_from_xml(data)
  end

That way, the spec for User#update_from_xml would have one less level of indirection, ie just

  @user.country.should equal(@country)

Hope this is useful,
Ashley


--
http://www.patchspace.co.uk/
http://aviewfromafar.net/



_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to