Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Rspec custom matchers for xml using Nokogiri XPath selector and optional content to match within selection. See matcher_spec.rb for usage.

Place have_xml.rb in your {Rails.root}/spec/support/matchers directory

Adapted from: Testing XML with rspec, xpath and libxml

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
describe "thing" do
context 'xml'
before do
get "/a_thing", {:format => :xml}
end
it "has a thing" do
response.body.should have_xml '/xpath/to/match' "content with xpath selector"
end
it "does not have another thing" do
response.body.should_not have_xml '/xpath/to/disallow' "content with xpath selector"
end
end
end
@benissimo
Copy link

benissimo commented Jan 29, 2013

Thanks for this, one note, if you're parsing xml with CDATA nodes and indentation, node.content will match the line breaks and whitespace in addition to the CDATA wrapped text. To handle that case, I rewrote your nodes.each section as:

  nodes.each do |node|
    cdata = node.children.find { |e| e.cdata? }
    value_to_check = node.text
    value_to_check = cdata.text if cdata
    value_to_check.should == text
  end

Without that change, have_xml fails if you try, for example, this assertion:

should have_xml("/Test/Property/Floorplan/Name", "FP One")

on XML such as:

<?xml version="1.0" encoding="utf-8"?>
<Test>
  <Property>
    <Floorplan>
      <Name>
        <![CDATA[FP One]]>
      </Name>
    </Floorplan>
  </Property>
 </Test>

p.s. line 19 of have_xml.rb should read:

  failure_message_for_should_not do |body|

(not |response|)

@pimpin
Copy link

pimpin commented Mar 2, 2016

I've made updates here

  • with expect syntax,
  • including @benissimo "CDATA unsensitive parsing feature"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment