使用HttpClient4实现API测试实战(1)
0、特别说明
2、测试API的多附件上传,请查阅
使用HttpClient4实现API测试实战(2)——多附件上传
1、引言
由于项目开发需要实现已有的API接口的测试,但API接口使用了token验证机制,使用soupui进行测试时,每次都需要先获取token,然后再进行登录,接着才能进行相关API接口的测试。显然后面的API接口测试是我们需要的,而获取token和登录都不是我们想要的,有没有办法跳过这两个步骤,直接进行API接口测试呢?
答案是肯定的,下面我们就使用HttpClient实现API测试进行实战。
2、新建测试项目
2.1添加项目依赖
commons-beanutils-1.8.0.jar commons-collections-3.2.1.jar commons-lang-2.4.jar commons-logging-1.0.4.jar dom4j-1.6.1.jar ezmorph-1.0.6.jar httpclient-4.0.1.jar httpcore-4.0.1.jar
2.2新建HttpClient帮助类HttpClientUtil
public class HttpClientUtil { public static DefaultHttpClient httpClient = null; public static HttpClient getInstance() { if (httpClient == null) { httpClient = new DefaultHttpClient(); } return httpClient; } public static void disconnect() { httpClient = null; } public static String doGet(String url) { return doGet(url, new ArrayList<BasicNameValuePair>()); } public static String doGet(String url, List<BasicNameValuePair> data) { /* 建立HTTP Post连线 */ HttpGet httpGet = new HttpGet(url); try { HttpResponse httpResponse = HttpClientUtil.getInstance().execute(httpGet); if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { return EntityUtils.toString(httpResponse.getEntity()); } else { System.out.println("doGet Error Response: " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { e.printStackTrace(); } return null; } public static String doPost(String url) { return doPost(url, new ArrayList<BasicNameValuePair>()); } public static String doPost(String url, List<BasicNameValuePair> data) { /* 建立HTTP Post连线 */ HttpPost httpPost = new HttpPost(url); try { // 发出HTTP request // httpPost.setEntity(new UrlEncodedFormEntity(data, HTTP.UTF_8)); httpPost.setEntity(new UrlEncodedFormEntity(data, "UTF-8")); // 取得HTTP response HttpResponse httpResponse = HttpClientUtil.getInstance().execute(httpPost); // 若状态码为200 ok if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // 取出回应字串 return EntityUtils.toString(httpResponse.getEntity()); } else { System.out.println("doPost Error Response: " + httpResponse.getStatusLine().toString()); } } catch (Exception e) { e.printStackTrace(); } return null; } }
2.3新建XML帮助类XmlUtil
public class XmlUtil { /** * 将xml格式的字符串转化成可以解析的Document对象 * * @param xml * @return */ public static Document parseXmlToDocument(String xml) { Document doc = null; if (xml != null && !xml.equals("")) { StringReader sr = new StringReader(xml); InputSource is = new InputSource(sr); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); doc = builder.parse(is); } catch (Exception e) { e.printStackTrace(); } } return doc; } // 从xml文件中获取节点的值 public static String getContentFromXml(String xml, String NodeName) { return getContentFromXml(xml, NodeName, 0); } public static String getContentFromXml(String xml, String NodeName, int index) { String value = ""; try { Document doc = XmlUtil.parseXmlToDocument(xml); if (doc != null) { Node node = doc.getElementsByTagName(NodeName).item(index); if (node != null) { value = node.getFirstChild().getTextContent(); } } } catch (Exception e) { return null; } return value; } }
2.4新建API帮助类ApiUtil
public class ApiUtil { private static final String OAUTH_COMSUMER_KEY = "key"; private static final String OAUTH_COMSUMER_SECRET = "password"; private static final String API_URL = "http://localhost/api"; private static String token = null; public static String getToken() { if (token == null) { token = accountToken(OAUTH_COMSUMER_KEY, OAUTH_COMSUMER_SECRET); } return token; } // Oauth的accountToken的获得 public static String accountToken(String key, String secret) { List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(0); params.add(new BasicNameValuePair("oauth_consumer_key", key)); params.add(new BasicNameValuePair("oauth_consumer_secret", secret)); String xml = HttpClientUtil.doPost(API_URL + "/accountToken", params); if (hasText(xml)) { if (xml.indexOf("errorCode") == -1) { return XmlUtil.getContentFromXml(xml, "accountToken"); } else { // 存在错误信息则返回null return null; } } else { return null; } } // 用户登录接口 public static boolean login(String username, String password) { return login(username, password, null); } public static boolean login(String username, String password, String userType) { List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(0); params.add(new BasicNameValuePair("account_token", getToken())); params.add(new BasicNameValuePair("username", username)); params.add(new BasicNameValuePair("password", password)); if (userType != null) { params.add(new BasicNameValuePair("userType", userType)); } String xml = HttpClientUtil.doPost(API_URL + "/login", params); if (!hasText(xml)) { return false; } if (xml.indexOf("errorCode") == -1) { return true; } else { return false; } } private static boolean hasText(String strText) { return strText != null && !"".equals(strText); } }
2.5ApiUtil中增加测试方法
public static void main(String[] argus) { System.out.println(ApiUtil.getToken()); ApiUtil.login("[email protected]", "password"); }
2.6运行测试
运行测试类后,出现“HTTP/1.1302MovedTemporarily”错误,但类似的代码在浏览器中执行没有问题,这究竟是什么原因造成?该如何解决呢?
3、302错误的原因及解决方法
(1)SOSO问问中“HTTP/1.1302MovedTemporarily”的内容如下:
应该是连接超时
302Movedtemporarily(redirect)你所连接的页面进行了Redirect
302Found类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“MovedTemporatily”,而HttpServletResponse中相应的常量是SC_MOVED_TEMPORARILY,而不是SC_FOUND。出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。为此,Servlet提供了一个专用的方法,即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。这是因为:
首先,代码更加简洁。
第二,使用sendRedirect,Servlet会自动构造一个包含新链接的页面(用于那些不能自动重定向的老式浏览器)。
最后,sendRedirect能够处理相对URL,自动把它们转换成绝对URL。
注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。
严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。
(2)从上面关于302错误的内容,联系到API服务器是采用了Nginx进行反向代理的,该错误应该是由于API接口由反向代理进行了重定向,从而导致出现302错误;
(3)Google相关的解决办法,在StackOverflow上找到了解决方法,在HttpClientUtil中获取HttpClient实例中增加重定向策略,代码如下
public static HttpClient getInstance() { if (httpClient == null) { httpClient = new DefaultHttpClient(); // 以下为新增内容 httpClient.setRedirectStrategy(new DefaultRedirectStrategy() { public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) { boolean isRedirect=false; try { isRedirect = super.isRedirected(request, response, context); } catch (Exception e) { e.printStackTrace(); } if (!isRedirect) { int responseCode = response.getStatusLine().getStatusCode(); if (responseCode == 301 || responseCode == 302) { return true; } } return isRedirect; } }); } return httpClient; }
(4)增加重定向策略代码后,发现新增的代码编译有错误,主要是相关的类或方法提示错误,查看相关的错误,发现主要是所使用的类或方法不存在,下载最新版本的HttpClient包替换后即可编译通过(4.2.1版本)
原依赖jar包
httpclient-4.0.1.jar httpcore-4.0.1.jar
替换后的依赖jar包
httpclient-4.2.1.jar httpcore-4.2.1.jar
(5)运行测试代码,登录接口login运行成功;
4、参考资料
[1]http://wenwen.soso.com/z/q90107388.htm
[2]http://stackoverflow.com/questions/9317604/httpclient-jsonobject
[3]http://stackoverflow.com/questions/3658721/httpclient-4-error-302-how-to-redirect