Permalink
Browse files

First commit. Implements include_xml and be_xml_eq RSpec matchers, al…

…so adds a cucumber step definition to test elements of a XML response
  • Loading branch information...
0 parents commit 8bc0328b67919200221b571a8b6c67837a3ee508 @fedegl fedegl committed Feb 20, 2012
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
@@ -0,0 +1,4 @@
+source 'http://rubygems.org'
+
+# Specify your gem's dependencies in xml_spec.gemspec
+gemspec
No changes.
@@ -0,0 +1,2 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
+require "xml_spec/version"
+require "xml_spec/helpers"
+require "xml_spec/matchers"
+require "xml_spec/nokogiri"
+
+module XmlSpec
+end
@@ -0,0 +1,7 @@
+require File.expand_path("../../xml_spec", __FILE__)
+
+World(XmlSpec::Helpers, XmlSpec::Matchers)
+
+Then /^the response should include the following XML$/ do |xml|
+ last_xml.should include_xml(xml)
+end
@@ -0,0 +1,14 @@
+require 'nokogiri'
+
+module XmlSpec
+ module Helpers
+ extend self
+
+ def parse_xml(subject, pattern)
+ [Nokogiri::XML.parse(subject).root, Nokogiri::XML.parse(pattern).root]
+ end
+
+ end
+end
+
+
@@ -0,0 +1,68 @@
+require "nokogiri"
+require "rspec"
+
+module XmlSpec
+ module Matchers
+ class BeXmlEql
+ include XmlSpec::Helpers
+
+ attr_reader :expected, :actual
+
+ def diffable?
+ true
+ end
+
+ def initialize(expected_xml)
+ @expected_xml = expected_xml
+ end
+
+ def matches?(actual_xml)
+ @actual, @expected = parse_xml(actual_xml, @expected_xml)
+ @actual.match?(@expected, true) && @expected.match?(@actual, true)
+ end
+
+ def failure_message_for_should
+ "the xml:\n#{@actual}\nshould exactly match xml:\n#{@expected}"
+ end
+
+ def failure_message_for_should_not
+ "the xml:\n#{@actual}\nshould not exactly match xml:\n#{@expected}\nbut it does"
+ end
+ end
+
+ class IncludeXml
+ include XmlSpec::Helpers
+
+ attr_reader :expected, :actual
+
+ def initialize(expected_xml)
+ @expected_xml = expected_xml
+ end
+
+ def matches?(actual_xml)
+ @actual, @expected = parse_xml(actual_xml, @expected_xml)
+ @actual.match?(expected, true)
+ end
+
+ def failure_message_for_should
+ "the xml:\n#{@actual}\nshould match xml structure:\n#{@expected}"
+ end
+
+ def failure_message_for_should_not
+ "the xml:\n#{@actual}\nshould not match xml structure:\n#{@expected}\nbut it does"
+ end
+ end
+
+ def be_xml_eql(xml)
+ XmlSpec::Matchers::BeXmlEql.new(xml)
+ end
+
+ def include_xml(xml)
+ XmlSpec::Matchers::IncludeXml.new(xml)
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.include XmlSpec::Matchers
+end
@@ -0,0 +1,5 @@
+require "xml_spec/nokogiri/node"
+
+class Nokogiri::XML::Node
+ include XmlSpec::NokogiriExt::Node
+end
@@ -0,0 +1,60 @@
+module XmlSpec
+ module NokogiriExt
+ module Node
+ def match?(element, compare_value = false)
+ if compare_value && element.leaf?
+ comparable_attributes == element.comparable_attributes and equal_text?(element)
+ else
+
+ #TODO clean this part of the code
+ if compare_value
+ (comparable_attributes == element.comparable_attributes) &&
+ contains_elements_of?(element) &&
+ element.elements.all? {|el| matches_at_least_one?(el, compare_value) }
+ else
+ contains_elements_of?(element) &&
+ element.elements.all? {|el| matches_at_least_one?(el, compare_value) }
+ end
+ end
+ end
+
+ def elements
+ children.collect {|node| node if node.element? }.delete_if {|node| node.nil?}
+ end
+
+ # Attributes of the current node.
+ def comparable_attributes
+ attributes.collect {|k,a| [k.downcase, a.value]}.sort
+ end
+
+ # Check if node is either childless, self-closing, or has content text.
+ def leaf?
+ children.size == 0 or (children.size == 1 && children.first.text?)
+ end
+
+ # def placeholder?
+ # TestXml.placeholders_enabled? and (content =~ /^`.*`$/)
+ # end
+
+ private
+ def equal_text?(element)
+ #element.content = content if element.placeholder?
+
+ content == element.content
+ end
+
+
+ def contains_elements_of?(element)
+ element.elements.find {|el| not contains?(el)}.nil?
+ end
+
+ def contains?(element)
+ children.find {|node| node.element? && node.name == element.name }
+ end
+
+ def matches_at_least_one?(element, compare_value)
+ search(element.name).find { |el| el.match?(element, compare_value) }
+ end
+ end
+ end
+end
@@ -0,0 +1,3 @@
+module XmlSpec
+ VERSION = "0.0.1"
+end
@@ -0,0 +1,4 @@
+require "xml_spec"
+
+RSpec.configure do |config|
+end
@@ -0,0 +1,33 @@
+require "spec_helper"
+
+describe "Matchers:" do
+ context "be_xml_eql" do
+ it "matches the exact same XML" do
+ xml = %(<name complete="true">TAPUHI</name><count type="integer">1</count>)
+ xml.should be_xml_eql(%(<name complete="true">TAPUHI</name><count type="integer">1</count>))
+ end
+
+ it "doesn't match XML with different attributes" do
+ xml = %(<name complete="true">TAPUHI</name><count type="integer">1</count>)
+ xml.should_not be_xml_eql(%(<name complete="false">TAPUHI</name><count type="integer">1</count>))
+ end
+ end
+
+ context "include_xml" do
+ it "matches a basic xml structure" do
+ xml = %(<values type="array">
+ <value>
+ <name>TAPUHI</name>
+ <count type="integer">1</count>
+ </value>
+ </values>)
+
+ xml.should include_xml(%(<values type="array"><value><name>TAPUHI</name></value></values>))
+ end
+
+ it "ignores other nodes" do
+ xml = %(<value><name>TAPUHI</name><count type="integer">1</count></value>)
+ xml.should include_xml(%(<value><name>TAPUHI</name></value>))
+ end
+ end
+end
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/xml_spec/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Federico Gonzalez"]
+ gem.email = ["fedegl@gmail.com"]
+ gem.description = %q{Test XML with RSpec and Cucumber}
+ gem.summary = %q{Test XML with RSpec and Cucumber}
+ gem.homepage = ""
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.name = "xml_spec"
+ gem.require_paths = ["lib"]
+ gem.version = XmlSpec::VERSION
+
+ gem.add_dependency "nokogiri", ">= 1.3.2"
+ gem.add_dependency "rspec", "~> 2.0"
+
+ gem.add_development_dependency "rake", "~> 0.9"
+ gem.add_development_dependency "cucumber", "~> 1.1", ">= 1.1.1"
+end

0 comments on commit 8bc0328

Please sign in to comment.