001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.util; 018 019import java.io.StringReader; 020import java.util.Map; 021 022import javax.xml.parsers.DocumentBuilder; 023import javax.xml.parsers.DocumentBuilderFactory; 024import javax.xml.transform.Source; 025import javax.xml.transform.dom.DOMSource; 026 027import org.hamcrest.Matcher; 028import org.w3c.dom.Document; 029import org.w3c.dom.Node; 030import org.xml.sax.InputSource; 031import org.xmlunit.builder.DiffBuilder; 032import org.xmlunit.diff.DefaultNodeMatcher; 033import org.xmlunit.diff.Diff; 034import org.xmlunit.diff.ElementSelectors; 035 036import static org.hamcrest.MatcherAssert.assertThat; 037 038/** 039 * A helper class for assertions on XML content. 040 * 041 * @author Rossen Stoyanchev 042 * @since 3.2 043 */ 044public class XmlExpectationsHelper { 045 046 /** 047 * Parse the content as {@link Node} and apply a {@link Matcher}. 048 */ 049 public void assertNode(String content, Matcher<? super Node> matcher) throws Exception { 050 Document document = parseXmlString(content); 051 assertThat("Body content", document, matcher); 052 } 053 054 private Document parseXmlString(String xml) throws Exception { 055 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 056 factory.setNamespaceAware(true); 057 DocumentBuilder documentBuilder = factory.newDocumentBuilder(); 058 InputSource inputSource = new InputSource(new StringReader(xml)); 059 return documentBuilder.parse(inputSource); 060 } 061 062 /** 063 * Parse the content as {@link DOMSource} and apply a {@link Matcher}. 064 * @see <a href="https://github.com/davidehringer/xml-matchers">xml-matchers</a> 065 */ 066 public void assertSource(String content, Matcher<? super Source> matcher) throws Exception { 067 Document document = parseXmlString(content); 068 assertThat("Body content", new DOMSource(document), matcher); 069 } 070 071 /** 072 * Parse the expected and actual content strings as XML and assert that the 073 * two are "similar" -- i.e. they contain the same elements and attributes 074 * regardless of order. 075 * <p>Use of this method assumes the 076 * <a href="https://github.com/xmlunit/xmlunit">XMLUnit</a> library is available. 077 * @param expected the expected XML content 078 * @param actual the actual XML content 079 * @see org.springframework.test.web.servlet.result.MockMvcResultMatchers#xpath(String, Object...) 080 * @see org.springframework.test.web.servlet.result.MockMvcResultMatchers#xpath(String, Map, Object...) 081 */ 082 public void assertXmlEqual(String expected, String actual) throws Exception { 083 XmlUnitDiff diff = new XmlUnitDiff(expected, actual); 084 if (diff.hasDifferences()) { 085 AssertionErrors.fail("Body content " + diff.toString()); 086 } 087 } 088 089 090 /** 091 * Inner class to prevent hard dependency on XML Unit. 092 */ 093 private static class XmlUnitDiff { 094 095 private final Diff diff; 096 097 098 XmlUnitDiff(String expected, String actual) { 099 this.diff = DiffBuilder.compare(expected).withTest(actual) 100 .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)) 101 .ignoreWhitespace().ignoreComments() 102 .checkForSimilar() 103 .build(); 104 } 105 106 107 public boolean hasDifferences() { 108 return this.diff.hasDifferences(); 109 } 110 111 @Override 112 public String toString() { 113 return this.diff.toString(); 114 } 115 116 } 117 118}