|
|
|
|
@ -29,6 +29,7 @@ import javax.xml.xpath.XPathExpressionException;
@@ -29,6 +29,7 @@ import javax.xml.xpath.XPathExpressionException;
|
|
|
|
|
import javax.xml.xpath.XPathFactory; |
|
|
|
|
|
|
|
|
|
import org.hamcrest.Matcher; |
|
|
|
|
import org.hamcrest.MatcherAssert; |
|
|
|
|
import org.w3c.dom.Document; |
|
|
|
|
import org.w3c.dom.Node; |
|
|
|
|
import org.w3c.dom.NodeList; |
|
|
|
|
@ -39,9 +40,6 @@ import org.springframework.util.CollectionUtils;
@@ -39,9 +40,6 @@ import org.springframework.util.CollectionUtils;
|
|
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
import org.springframework.util.xml.SimpleNamespaceContext; |
|
|
|
|
|
|
|
|
|
import static org.hamcrest.MatcherAssert.assertThat; |
|
|
|
|
import static org.springframework.test.util.AssertionErrors.assertEquals; |
|
|
|
|
import static org.springframework.test.util.AssertionErrors.assertTrue; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* A helper class for applying assertions via XPath expressions. |
|
|
|
|
@ -74,9 +72,8 @@ public class XpathExpectationsHelper {
@@ -74,9 +72,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
this.hasNamespaces = !CollectionUtils.isEmpty(namespaces); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private XPathExpression compileXpathExpression(String expression, @Nullable Map<String, String> namespaces) |
|
|
|
|
throws XPathExpressionException { |
|
|
|
|
private static XPathExpression compileXpathExpression(String expression, |
|
|
|
|
@Nullable Map<String, String> namespaces) throws XPathExpressionException { |
|
|
|
|
|
|
|
|
|
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext(); |
|
|
|
|
namespaceContext.setBindings(namespaces != null ? namespaces : Collections.emptyMap()); |
|
|
|
|
@ -85,6 +82,7 @@ public class XpathExpectationsHelper {
@@ -85,6 +82,7 @@ public class XpathExpectationsHelper {
|
|
|
|
|
return xpath.compile(expression); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Return the compiled XPath expression. |
|
|
|
|
*/ |
|
|
|
|
@ -92,6 +90,7 @@ public class XpathExpectationsHelper {
@@ -92,6 +90,7 @@ public class XpathExpectationsHelper {
|
|
|
|
|
return this.xpathExpression; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Parse the content, evaluate the XPath expression as a {@link Node}, |
|
|
|
|
* and assert it with the given {@code Matcher<Node>}. |
|
|
|
|
@ -99,38 +98,8 @@ public class XpathExpectationsHelper {
@@ -99,38 +98,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
public void assertNode(byte[] content, @Nullable String encoding, final Matcher<? super Node> matcher) |
|
|
|
|
throws Exception { |
|
|
|
|
|
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class); |
|
|
|
|
assertThat("XPath " + this.expression, node, matcher); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Parse the given XML content to a {@link Document}. |
|
|
|
|
* @param xml the content to parse |
|
|
|
|
* @param encoding optional content encoding, if provided as metadata (e.g. in HTTP headers) |
|
|
|
|
* @return the parsed document |
|
|
|
|
*/ |
|
|
|
|
protected Document parseXmlByteArray(byte[] xml, @Nullable String encoding) throws Exception { |
|
|
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
|
|
|
|
factory.setNamespaceAware(this.hasNamespaces); |
|
|
|
|
DocumentBuilder documentBuilder = factory.newDocumentBuilder(); |
|
|
|
|
InputSource inputSource = new InputSource(new ByteArrayInputStream(xml)); |
|
|
|
|
if (StringUtils.hasText(encoding)) { |
|
|
|
|
inputSource.setEncoding(encoding); |
|
|
|
|
} |
|
|
|
|
return documentBuilder.parse(inputSource); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Apply the XPath expression to given document. |
|
|
|
|
* @throws XPathExpressionException if expression evaluation failed |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
@Nullable |
|
|
|
|
protected <T> T evaluateXpath(Document document, QName evaluationType, Class<T> expectedClass) |
|
|
|
|
throws XPathExpressionException { |
|
|
|
|
|
|
|
|
|
return (T) getXpathExpression().evaluate(document, evaluationType); |
|
|
|
|
Node node = evaluateXpath(content, encoding, Node.class); |
|
|
|
|
MatcherAssert.assertThat("XPath " + this.expression, node, matcher); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -138,9 +107,8 @@ public class XpathExpectationsHelper {
@@ -138,9 +107,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void exists(byte[] content, @Nullable String encoding) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class); |
|
|
|
|
assertTrue("XPath " + this.expression + " does not exist", node != null); |
|
|
|
|
Node node = evaluateXpath(content, encoding, Node.class); |
|
|
|
|
AssertionErrors.assertTrue("XPath " + this.expression + " does not exist", node != null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -148,9 +116,8 @@ public class XpathExpectationsHelper {
@@ -148,9 +116,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void doesNotExist(byte[] content, @Nullable String encoding) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
Node node = evaluateXpath(document, XPathConstants.NODE, Node.class); |
|
|
|
|
assertTrue("XPath " + this.expression + " exists", node == null); |
|
|
|
|
Node node = evaluateXpath(content, encoding, Node.class); |
|
|
|
|
AssertionErrors.assertTrue("XPath " + this.expression + " exists", node == null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -158,11 +125,12 @@ public class XpathExpectationsHelper {
@@ -158,11 +125,12 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* given Hamcrest matcher. |
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertNodeCount(byte[] content, @Nullable String encoding, Matcher<Integer> matcher) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
NodeList nodeList = evaluateXpath(document, XPathConstants.NODESET, NodeList.class); |
|
|
|
|
assertThat("nodeCount for XPath " + this.expression, |
|
|
|
|
(nodeList != null ? nodeList.getLength() : 0), matcher); |
|
|
|
|
public void assertNodeCount(byte[] content, @Nullable String encoding, Matcher<Integer> matcher) |
|
|
|
|
throws Exception { |
|
|
|
|
|
|
|
|
|
NodeList nodeList = evaluateXpath(content, encoding, NodeList.class); |
|
|
|
|
String reason = "nodeCount for XPath " + this.expression; |
|
|
|
|
MatcherAssert.assertThat(reason, nodeList != null ? nodeList.getLength() : 0, matcher); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -170,9 +138,8 @@ public class XpathExpectationsHelper {
@@ -170,9 +138,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertNodeCount(byte[] content, @Nullable String encoding, int expectedCount) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
NodeList nodeList = evaluateXpath(document, XPathConstants.NODESET, NodeList.class); |
|
|
|
|
assertEquals("nodeCount for XPath " + this.expression, expectedCount, |
|
|
|
|
NodeList nodeList = evaluateXpath(content, encoding, NodeList.class); |
|
|
|
|
AssertionErrors.assertEquals("nodeCount for XPath " + this.expression, expectedCount, |
|
|
|
|
(nodeList != null ? nodeList.getLength() : 0)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -181,10 +148,11 @@ public class XpathExpectationsHelper {
@@ -181,10 +148,11 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* given Hamcrest matcher. |
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertString(byte[] content, @Nullable String encoding, Matcher<? super String> matcher) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
String result = evaluateXpath(document, XPathConstants.STRING, String.class); |
|
|
|
|
assertThat("XPath " + this.expression, result, matcher); |
|
|
|
|
public void assertString(byte[] content, @Nullable String encoding, Matcher<? super String> matcher) |
|
|
|
|
throws Exception { |
|
|
|
|
|
|
|
|
|
String actual = evaluateXpath(content, encoding, String.class); |
|
|
|
|
MatcherAssert.assertThat("XPath " + this.expression, actual, matcher); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -192,9 +160,8 @@ public class XpathExpectationsHelper {
@@ -192,9 +160,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertString(byte[] content, @Nullable String encoding, String expectedValue) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
String actual = evaluateXpath(document, XPathConstants.STRING, String.class); |
|
|
|
|
assertEquals("XPath " + this.expression, expectedValue, actual); |
|
|
|
|
String actual = evaluateXpath(content, encoding, String.class); |
|
|
|
|
AssertionErrors.assertEquals("XPath " + this.expression, expectedValue, actual); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -203,9 +170,8 @@ public class XpathExpectationsHelper {
@@ -203,9 +170,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertNumber(byte[] content, @Nullable String encoding, Matcher<? super Double> matcher) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
Double result = evaluateXpath(document, XPathConstants.NUMBER, Double.class); |
|
|
|
|
assertThat("XPath " + this.expression, result, matcher); |
|
|
|
|
Double actual = evaluateXpath(content, encoding, Double.class); |
|
|
|
|
MatcherAssert.assertThat("XPath " + this.expression, actual, matcher); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -213,9 +179,8 @@ public class XpathExpectationsHelper {
@@ -213,9 +179,8 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertNumber(byte[] content, @Nullable String encoding, Double expectedValue) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
Double actual = evaluateXpath(document, XPathConstants.NUMBER, Double.class); |
|
|
|
|
assertEquals("XPath " + this.expression, expectedValue, actual); |
|
|
|
|
Double actual = evaluateXpath(content, encoding, Double.class); |
|
|
|
|
AssertionErrors.assertEquals("XPath " + this.expression, expectedValue, actual); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -223,9 +188,76 @@ public class XpathExpectationsHelper {
@@ -223,9 +188,76 @@ public class XpathExpectationsHelper {
|
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
*/ |
|
|
|
|
public void assertBoolean(byte[] content, @Nullable String encoding, boolean expectedValue) throws Exception { |
|
|
|
|
String actual = evaluateXpath(content, encoding, String.class); |
|
|
|
|
AssertionErrors.assertEquals("XPath " + this.expression, expectedValue, Boolean.parseBoolean(actual)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Evaluate the XPath and return the resulting value. |
|
|
|
|
* @param content the content to evaluate against |
|
|
|
|
* @param encoding the encoding to use (optionally) |
|
|
|
|
* @param targetClass the target class, one of Number, String, Boolean, |
|
|
|
|
* org.w3c.Node, or NodeList |
|
|
|
|
* @throws Exception if content parsing or expression evaluation fails |
|
|
|
|
* @since 5.1 |
|
|
|
|
*/ |
|
|
|
|
@Nullable |
|
|
|
|
public <T> T evaluateXpath(byte[] content, @Nullable String encoding, Class<T> targetClass) throws Exception { |
|
|
|
|
Document document = parseXmlByteArray(content, encoding); |
|
|
|
|
String actual = evaluateXpath(document, XPathConstants.STRING, String.class); |
|
|
|
|
assertEquals("XPath " + this.expression, expectedValue, Boolean.parseBoolean(actual)); |
|
|
|
|
return evaluateXpath(document, toQName(targetClass), targetClass); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Parse the given XML content to a {@link Document}. |
|
|
|
|
* @param xml the content to parse |
|
|
|
|
* @param encoding optional content encoding, if provided as metadata (e.g. in HTTP headers) |
|
|
|
|
* @return the parsed document |
|
|
|
|
*/ |
|
|
|
|
protected Document parseXmlByteArray(byte[] xml, @Nullable String encoding) throws Exception { |
|
|
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
|
|
|
|
factory.setNamespaceAware(this.hasNamespaces); |
|
|
|
|
DocumentBuilder documentBuilder = factory.newDocumentBuilder(); |
|
|
|
|
InputSource inputSource = new InputSource(new ByteArrayInputStream(xml)); |
|
|
|
|
if (StringUtils.hasText(encoding)) { |
|
|
|
|
inputSource.setEncoding(encoding); |
|
|
|
|
} |
|
|
|
|
return documentBuilder.parse(inputSource); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Apply the XPath expression to given document. |
|
|
|
|
* @throws XPathExpressionException if expression evaluation failed |
|
|
|
|
*/ |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
@Nullable |
|
|
|
|
protected <T> T evaluateXpath(Document document, QName evaluationType, Class<T> expectedClass) |
|
|
|
|
throws XPathExpressionException { |
|
|
|
|
|
|
|
|
|
return (T) getXpathExpression().evaluate(document, evaluationType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private <T> QName toQName(Class<T> expectedClass) { |
|
|
|
|
QName evaluationType; |
|
|
|
|
if (Number.class.isAssignableFrom(expectedClass)) { |
|
|
|
|
evaluationType = XPathConstants.NUMBER; |
|
|
|
|
} |
|
|
|
|
else if (CharSequence.class.isAssignableFrom(expectedClass)) { |
|
|
|
|
evaluationType = XPathConstants.STRING; |
|
|
|
|
} |
|
|
|
|
else if (Boolean.class.isAssignableFrom(expectedClass)) { |
|
|
|
|
evaluationType = XPathConstants.BOOLEAN; |
|
|
|
|
} |
|
|
|
|
else if (Node.class.isAssignableFrom(expectedClass)) { |
|
|
|
|
evaluationType = XPathConstants.NODE; |
|
|
|
|
} |
|
|
|
|
else if (NodeList.class.isAssignableFrom(expectedClass)) { |
|
|
|
|
evaluationType = XPathConstants.NODESET; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
throw new IllegalArgumentException("Unexpected target class " + expectedClass + ". " + |
|
|
|
|
"Supported: numbers, strings, boolean, and org.w3c.Node and NodeList"); |
|
|
|
|
} |
|
|
|
|
return evaluationType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|