Hi all,
I've seen some demand for testing RESTful services (for example useful
for testing AtomPub servers) with webtest. Currently, this cannot be
done using webtest because "invoke" does not allow to change the content
type for POST requests; Plus, HTTP method PUT cannot be used to send
content.
Marc said earlier a patch would be welcome
(http://lists.canoo.com/pipermail/webtest/2008q1/010021.html):
> I don't think that you will be able to send a body currently with PUT
for instance. Any patch is welcome.
So, I've attached a patch that solves this problem and makes webtest
usable for any RESTful service (a door opener for a huge comunity). The
patch should be 100% backwards compatible with hitherto functionality.
It adds an additional attribute "contentType" which can be used to
overwrite the content-type HTTP header. Also, I've enabled the PUT
method to send body data within this patch.
I hope this is helpful for others as well and would be happy if it makes
it into the trunk. Thank you for all your fantastic work.
cheers,
Dennis
--
Dennis Knochenwefel
Software Architect
28msec Inc.
http://www.28msec.com
http://twitter.com/28msec
Index: src/main/java/com/canoo/webtest/steps/request/InvokePage.java
===================================================================
--- src/main/java/com/canoo/webtest/steps/request/InvokePage.java
(Revision 117476)
+++ src/main/java/com/canoo/webtest/steps/request/InvokePage.java
(Arbeitskopie)
@@ -38,6 +38,7 @@
private String fMethod = "GET";
private File fContentFile;
private String fContent;
+ private String fContentType;
private String fSoapAction;
@@ -48,8 +49,25 @@
public String getUrl() {
return fUrl;
}
+
+ public String getContentType() {
+ return fContentType;
+ }
/**
+ * Sets the HTTP Content Type.
+ *
+ * @param contentType
+ * @webtest.parameter
+ * required="no"
+ * description="Sets the Content type of the request. Defaults to
'application/x-www-form-urlencoded' for a POST request."
+ */
+ public void setContentType(final String contentType) {
+ fContentType = contentType;
+ }
+
+ /**
* Alternative to set the content of a SOAP message.
* @param txt the content
* @webtest.nested.parameter
@@ -86,7 +104,7 @@
* @webtest.parameter
* required="no"
* default="GET"
- * description="Sets the HTTP Method, i.e. whether the invoke is a GET
or POST."
+ * description="Sets the HTTP Method, i.e. whether the invoke is a GET
or POST. Also, more advanced HTTP methods like PUT, DELETE, HEAD, or OPTIONS
can be used."
*/
public void setMethod(final String method) {
fMethod = method;
@@ -143,40 +161,58 @@
}
protected void verifyParameters() {
- super.verifyParameters();
- nullParamCheck(getUrl(), "url");
- paramCheck(getContent() != null && getContentFile() != null, "Only one
of 'content' and 'contentFile' must be set.");
- paramCheck("POST".equals(getMethod()) && getContent() == null &&
getContentFile() == null,
- "One of 'content' or 'contentFile' must be set for POST.");
+ super.verifyParameters();
+ nullParamCheck(getUrl(), "url");
+ paramCheck(getContent() != null && getContentFile() != null,
+ "Only one of 'content' and 'contentFile' must be set.");
+
+ String method=getMethod().toUpperCase();
+ paramCheck(!"POST".equals(method)
+ && !"GET".equals(method)
+ && !"PUT".equals(method)
+ && !"DELETE".equals(method)
+ && !"HEAD".equals(method)
+ && !"OPTIONS".equals(method),
+ "'method' must be one of GET, POST, PUT, DELETE, HEAD, or
OPTIONS.");
+ paramCheck("POST".equals(method) && getContent() == null &&
getContentFile() == null,
+ "One of 'content' or 'contentFile' must be set for POST
requests.");
+ paramCheck("PUT".equals(method) && getContent() == null &&
getContentFile() == null,
+ "One of 'content' or 'contentFile' must be set for PUT
requests.");
+ paramCheck("GET".equals(method) && !(getContent() == null &&
getContentFile() == null),
+ "'content' and 'contentFile' must not be set for GET
requests.");
+ paramCheck("DELETE".equals(method) && !(getContent() == null &&
getContentFile() == null),
+ "'content' and 'contentFile' must not be set for GET
requests.");
+
}
protected Page findTarget() throws IOException, SAXException {
- if ("POST".equals(getMethod())) {
- return findTargetByPost();
+ fCompleteUrl = getContext().getConfig().getUrlForPage(getUrl());
+ final WebRequest request = new WebRequest(new URL(fCompleteUrl));
+ request.setHttpMethod(HttpMethod.valueOf(getMethod().toUpperCase()));
+ if ("POST".equals(getMethod().toUpperCase()) ||
"PUT".equals(getMethod().toUpperCase())) {
+ return findTargetByPostOrPut(request);
}
- fCompleteUrl = getContext().getConfig().getUrlForPage(getUrl());
- final WebRequest settings = new WebRequest(new URL(fCompleteUrl));
- settings.setHttpMethod(HttpMethod.valueOf(getMethod().toUpperCase()));
- return getResponse(settings);
+ return getResponse(request);
}
- private Page findTargetByPost() throws IOException, SAXException {
- String url = getContext().getConfig().getUrlForPage(getUrl());
- final WebRequest settings = new WebRequest(new URL(url),
HttpMethod.POST);
-
- // get default encoding
+ private Page findTargetByPostOrPut(WebRequest request) throws IOException,
SAXException {
+ // get default encoding
final String charset = System.getProperty("file.encoding");
final Map headers = new HashMap();
if (!StringUtils.isEmpty(fSoapAction)) {
+
headers.put("Content-type", "text/xml; charset=" + charset);
headers.put("SOAPAction", fSoapAction);
- }
+ }
+ else if(!StringUtils.isEmpty(fContentType)) {
+ headers.put("Content-type", fContentType);
+ }
else {
- // TODO: is this the correct Content-type for non-SOAP posts?
headers.put("Content-type", "application/x-www-form-urlencoded");
}
- settings.setAdditionalHeaders(headers);
+
+ request.setAdditionalHeaders(headers);
final String content;
if (getContent() != null) {
content = getContent();
@@ -184,9 +220,9 @@
else {
content = FileUtil.readFileToString(getContentFile(), this);
}
- settings.setRequestBody(content);
- settings.setCharset(charset);
- return getResponse(settings);
+ request.setRequestBody(content);
+ request.setCharset(charset);
+ return getResponse(request);
}
protected String getLogMessageForTarget() {