Skip to content

Commit

Permalink
Fix issue with xpath assertions in Spring Test MVC
Browse files Browse the repository at this point in the history
Issue: SPR-10704
  • Loading branch information
rstoyanchev committed Jul 19, 2013
1 parent 82ec06a commit 7bab879
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package org.springframework.test.util;

import static org.springframework.test.util.AssertionErrors.*;
import static org.springframework.test.util.MatcherAssertionErrors.*;

import java.io.StringReader;
import java.util.Collections;
import java.util.Map;
Expand All @@ -33,12 +30,16 @@
import javax.xml.xpath.XPathFactory;

import org.hamcrest.Matcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.xml.SimpleNamespaceContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import static org.springframework.test.util.AssertionErrors.*;
import static org.springframework.test.util.MatcherAssertionErrors.*;

/**
* A helper class for applying assertions via XPath expressions.
*
Expand All @@ -51,6 +52,8 @@ public class XpathExpectationsHelper {

private final XPathExpression xpathExpression;

private final boolean hasNamespaces;


/**
* Class constructor.
Expand All @@ -66,6 +69,7 @@ public XpathExpectationsHelper(String expression, Map<String, String> namespaces

this.expression = String.format(expression, args);
this.xpathExpression = compileXpathExpression(this.expression, namespaces);
this.hasNamespaces = !CollectionUtils.isEmpty(namespaces);
}

private XPathExpression compileXpathExpression(String expression, Map<String, String> namespaces)
Expand Down Expand Up @@ -104,7 +108,7 @@ public void assertNode(String content, final Matcher<? super Node> matcher) thro
*/
protected Document parseXmlString(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setNamespaceAware(this.hasNamespaces);
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
Document document = documentBuilder.parse(inputSource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@

package org.springframework.test.web.servlet.samples.standalone.resultmatchers;

import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -47,6 +36,12 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.web.bind.annotation.RequestMethod.*;

/**
* Examples of expectations on XML response content with XPath expressions.
*
Expand All @@ -57,7 +52,7 @@
*/
public class XpathAssertionTests {

private static final Map<String, String> NS =
private static final Map<String, String> musicNamespace =
Collections.singletonMap("ns", "http://example.org/music/people");

private MockMvc mockMvc;
Expand All @@ -78,13 +73,13 @@ public void testExists() throws Exception {
String performer = "/ns:people/performers/performer[%s]";

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath(composer, NS, 1).exists())
.andExpect(xpath(composer, NS, 2).exists())
.andExpect(xpath(composer, NS, 3).exists())
.andExpect(xpath(composer, NS, 4).exists())
.andExpect(xpath(performer, NS, 1).exists())
.andExpect(xpath(performer, NS, 2).exists())
.andExpect(xpath(composer, NS, 1).node(notNullValue()));
.andExpect(xpath(composer, musicNamespace, 1).exists())
.andExpect(xpath(composer, musicNamespace, 2).exists())
.andExpect(xpath(composer, musicNamespace, 3).exists())
.andExpect(xpath(composer, musicNamespace, 4).exists())
.andExpect(xpath(performer, musicNamespace, 1).exists())
.andExpect(xpath(performer, musicNamespace, 2).exists())
.andExpect(xpath(composer, musicNamespace, 1).node(notNullValue()));
}

@Test
Expand All @@ -94,11 +89,11 @@ public void testDoesNotExist() throws Exception {
String performer = "/ns:people/performers/performer[%s]";

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath(composer, NS, 0).doesNotExist())
.andExpect(xpath(composer, NS, 5).doesNotExist())
.andExpect(xpath(performer, NS, 0).doesNotExist())
.andExpect(xpath(performer, NS, 3).doesNotExist())
.andExpect(xpath(composer, NS, 0).node(nullValue()));
.andExpect(xpath(composer, musicNamespace, 0).doesNotExist())
.andExpect(xpath(composer, musicNamespace, 5).doesNotExist())
.andExpect(xpath(performer, musicNamespace, 0).doesNotExist())
.andExpect(xpath(performer, musicNamespace, 3).doesNotExist())
.andExpect(xpath(composer, musicNamespace, 0).node(nullValue()));
}

@Test
Expand All @@ -108,15 +103,15 @@ public void testString() throws Exception {
String performerName = "/ns:people/performers/performer[%s]/name";

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath(composerName, NS, 1).string("Johann Sebastian Bach"))
.andExpect(xpath(composerName, NS, 2).string("Johannes Brahms"))
.andExpect(xpath(composerName, NS, 3).string("Edvard Grieg"))
.andExpect(xpath(composerName, NS, 4).string("Robert Schumann"))
.andExpect(xpath(performerName, NS, 1).string("Vladimir Ashkenazy"))
.andExpect(xpath(performerName, NS, 2).string("Yehudi Menuhin"))
.andExpect(xpath(composerName, NS, 1).string(equalTo("Johann Sebastian Bach"))) // Hamcrest..
.andExpect(xpath(composerName, NS, 1).string(startsWith("Johann")))
.andExpect(xpath(composerName, NS, 1).string(notNullValue()));
.andExpect(xpath(composerName, musicNamespace, 1).string("Johann Sebastian Bach"))
.andExpect(xpath(composerName, musicNamespace, 2).string("Johannes Brahms"))
.andExpect(xpath(composerName, musicNamespace, 3).string("Edvard Grieg"))
.andExpect(xpath(composerName, musicNamespace, 4).string("Robert Schumann"))
.andExpect(xpath(performerName, musicNamespace, 1).string("Vladimir Ashkenazy"))
.andExpect(xpath(performerName, musicNamespace, 2).string("Yehudi Menuhin"))
.andExpect(xpath(composerName, musicNamespace, 1).string(equalTo("Johann Sebastian Bach"))) // Hamcrest..
.andExpect(xpath(composerName, musicNamespace, 1).string(startsWith("Johann")))
.andExpect(xpath(composerName, musicNamespace, 1).string(notNullValue()));
}

@Test
Expand All @@ -125,12 +120,12 @@ public void testNumber() throws Exception {
String composerDouble = "/ns:people/composers/composer[%s]/someDouble";

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath(composerDouble, NS, 1).number(21d))
.andExpect(xpath(composerDouble, NS, 2).number(.0025))
.andExpect(xpath(composerDouble, NS, 3).number(1.6035))
.andExpect(xpath(composerDouble, NS, 4).number(Double.NaN))
.andExpect(xpath(composerDouble, NS, 1).number(equalTo(21d))) // Hamcrest..
.andExpect(xpath(composerDouble, NS, 3).number(closeTo(1.6, .01)));
.andExpect(xpath(composerDouble, musicNamespace, 1).number(21d))
.andExpect(xpath(composerDouble, musicNamespace, 2).number(.0025))
.andExpect(xpath(composerDouble, musicNamespace, 3).number(1.6035))
.andExpect(xpath(composerDouble, musicNamespace, 4).number(Double.NaN))
.andExpect(xpath(composerDouble, musicNamespace, 1).number(equalTo(21d))) // Hamcrest..
.andExpect(xpath(composerDouble, musicNamespace, 3).number(closeTo(1.6, .01)));
}

@Test
Expand All @@ -139,20 +134,36 @@ public void testBoolean() throws Exception {
String performerBooleanValue = "/ns:people/performers/performer[%s]/someBoolean";

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath(performerBooleanValue, NS, 1).booleanValue(false))
.andExpect(xpath(performerBooleanValue, NS, 2).booleanValue(true));
.andExpect(xpath(performerBooleanValue, musicNamespace, 1).booleanValue(false))
.andExpect(xpath(performerBooleanValue, musicNamespace, 2).booleanValue(true));
}

@Test
public void testNodeCount() throws Exception {

this.mockMvc.perform(get("/music/people"))
.andExpect(xpath("/ns:people/composers/composer", NS).nodeCount(4))
.andExpect(xpath("/ns:people/performers/performer", NS).nodeCount(2))
.andExpect(xpath("/ns:people/composers/composer", NS).nodeCount(equalTo(4))) // Hamcrest..
.andExpect(xpath("/ns:people/performers/performer", NS).nodeCount(equalTo(2)));
.andExpect(xpath("/ns:people/composers/composer", musicNamespace).nodeCount(4))
.andExpect(xpath("/ns:people/performers/performer", musicNamespace).nodeCount(2))
.andExpect(xpath("/ns:people/composers/composer", musicNamespace).nodeCount(equalTo(4))) // Hamcrest..
.andExpect(xpath("/ns:people/performers/performer", musicNamespace).nodeCount(equalTo(2)));
}

// SPR-10704

@Test
public void testFeedWithLinefeedChars() throws Exception {

// Map<String, String> namespace = Collections.singletonMap("ns", "");

standaloneSetup(new BlogFeedController()).build()
.perform(get("/blog.atom").accept(MediaType.APPLICATION_ATOM_XML))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_ATOM_XML))
.andExpect(xpath("//feed/title").string("Test Feed"))
.andExpect(xpath("//feed/icon").string("http://www.example.com/favicon.ico"));
}


@Controller
private static class MusicController {

Expand Down Expand Up @@ -203,4 +214,19 @@ public List<Person> getPerformers() {
}
}


@Controller
public class BlogFeedController {

@RequestMapping(value="/blog.atom", method = { GET, HEAD })
@ResponseBody
public String listPublishedPosts() {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">\r\n"
+ " <title>Test Feed</title>\r\n"
+ " <icon>http://www.example.com/favicon.ico</icon>\r\n"
+ "</feed>\r\n\r\n";
}
}

}

0 comments on commit 7bab879

Please sign in to comment.