17

I have an RSS feed that I am writing an RSpec test for. I want to test that the XML document has the correct nodes and structure. Unfortunately, I can't find any good examples of how to do this in a clean way. I have only found some half-implemented solutions and outdated blog posts. How can I test the structure of an XML document using RSpec?

Andrew
  • 227,796
  • 193
  • 515
  • 708

4 Answers4

8

Hi I can recommend you to use custom matcher for this.

 require 'nokogiri' 
    RSpec::Matchers.define :have_xml do |xpath, text|   
      match do |body|
        doc = Nokogiri::XML::Document.parse(body)
        nodes = doc.xpath(xpath)
        nodes.empty?.should be_false
        if text
          nodes.each do |node|
            node.content.should == text
          end
        end
        true   
      end

      failure_message_for_should do |body|
        "expected to find xml tag #{xpath} in:\n#{body}"   
      end

      failure_message_for_should_not do |response|
        "expected not to find xml tag #{xpath} in:\n#{body}"   
      end

      description do
        "have xml tag #{xpath}"   
      end 
   end

Full example can be found here https://gist.github.com/Fivell/8025849

Fivell
  • 11,829
  • 3
  • 61
  • 99
6

No longer necessary to roll your own. We deal this problem daily, using the equivalent-xml matcher at https://github.com/mbklein/equivalent-xml .

require 'rspec/matchers'
require 'equivalent-xml'
...
expect(node_1).to be_equivalent_to(node_2)

Has options for edge cases like whitespace-preservation.

Your other option is to use a formal XSD template for strict validation.

Joe Atzberger
  • 3,079
  • 1
  • 18
  • 16
2
context 'POST #join' do
    it 'does successfully hit join xml route' do
      post :join,
        format: :xml
      response.content_type.should == "application/xml"
      response.should be_ok
    end
end

This worked for me. I didn't realize I had to pass format: :xml. My join route responds to /join.xml and I was testing that this was successful.

Angie
  • 107
  • 6
  • 3
    This doesn't really solve the OP's question, but it was helpful for me to just assert you are getting xml without adding gems – tam5 May 31 '16 at 15:29
1

Give Approvals a try, it works with rspec, I have used for testing Json payload, and it is used with Minitest in exercism.io

EDIT

  it "returns available traffic information around me" do
    post '/search_traffic_around', {location: [-87.688219, 41.941149]}.to_json
    output = last_response.body
    options = {format: :json, name: 'traffic_around_location'}
    Approvals.verify(output,options)
  end

the JSON I am verifying against is located in spec/fixtures folder named traffic_around_location.approved.json

Implementation where the above snippet is pulled from is available here

How it works is you supply it an expected Payload, JSON, XML, TXT and HTML this I am sure it supports in spec/fixtures and when you run the test it checks to confirm that the payload received matches the expected(approved) payload the test would pass if it matches else the test fails

bjhaid
  • 9,592
  • 2
  • 37
  • 47
  • Can you provide an example of how this is meant to be used? I have read through the Approvals readme and it's not very clear. – Andrew Dec 16 '13 at 21:49
  • I'll give you an up-vote for updating your answer but I'd still like to see some alternatives (or at least some more up-votes indicating this is a popular solution). – Andrew Dec 16 '13 at 23:53