`
cgs1999
  • 浏览: 530391 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

使用HttpClient4实现API测试实战(1)

    博客分类:
  • J2EE
阅读更多
0、特别说明
1、声明:如需转载,请注明来自 http://cgs1999.iteye.com/;
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.5 ApiUtil中增加测试方法
public static void main(String[] argus) {
	System.out.println(ApiUtil.getToken());

	ApiUtil.login("chengesheng@gmail.com", "password");
}


2.6 运行测试
运行测试类后,出现“HTTP/1.1 302 Moved Temporarily”错误,但类似的代码在浏览器中执行没有问题,这究竟是什么原因造成?该如何解决呢?

3、302错误的原因及解决方法
(1)SOSO问问中“HTTP/1.1 302 Moved Temporarily”的内容如下:
引用

应该是连接超时
302 Moved temporarily (redirect) 你所连接的页面进行了Redirect

302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”,而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相关的解决办法,在Stack Overflow上找到了解决方法,在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





2
0
分享到:
评论
7 楼 cgs1999 2014-10-23  
bolo 写道
ProxyUtil是个什么类?


这应该是XmlUtil,之前的代码重构后没有修改过来
6 楼 bolo 2014-10-22  
ProxyUtil是个什么类?
5 楼 BigBird2012 2014-07-16  
cgs1999 写道
BigBird2012 写道
cgs1999 写道
BigBird2012 写道
这篇文章真不错,详细全面的介绍了新的HttpClient的用法和该注意的问题。HttpClientUtil 这个工具类很好。想请教楼主一个问题,如何更好的使用HttpClient进行项目的并发性能测试呢?或者我们能交流一下使用HttpClient进行测试的方法吗?Thank楼主,期待回复!!


该方法个人觉得比较适合API开发人员自测,若要测试API的并发性能建议使用专业的测试工具,如LoadRunner、QTP。专业测试工具可以模拟指定数量的用户并发操作,并最终给出详细的测试报告。

我也建议公司采用这样的方式,但我们是小公司,领导嫌学习测试工具成本高,周期长,不愿意这样做,我也是无奈啊



还是建议使用测试工具,QTP入门简单使用的学习成本应该还好,可以考虑

若要自己写测试的工具,在模拟并发用户时需多线程以及相关模拟测试的线程安全问题,开发起来还是有很多内容需要考虑

嗯,谢谢,我看看你说的这些
4 楼 cgs1999 2014-07-16  
BigBird2012 写道
cgs1999 写道
BigBird2012 写道
这篇文章真不错,详细全面的介绍了新的HttpClient的用法和该注意的问题。HttpClientUtil 这个工具类很好。想请教楼主一个问题,如何更好的使用HttpClient进行项目的并发性能测试呢?或者我们能交流一下使用HttpClient进行测试的方法吗?Thank楼主,期待回复!!


该方法个人觉得比较适合API开发人员自测,若要测试API的并发性能建议使用专业的测试工具,如LoadRunner、QTP。专业测试工具可以模拟指定数量的用户并发操作,并最终给出详细的测试报告。

我也建议公司采用这样的方式,但我们是小公司,领导嫌学习测试工具成本高,周期长,不愿意这样做,我也是无奈啊



还是建议使用测试工具,QTP入门简单使用的学习成本应该还好,可以考虑

若要自己写测试的工具,在模拟并发用户时需多线程以及相关模拟测试的线程安全问题,开发起来还是有很多内容需要考虑
3 楼 BigBird2012 2014-07-16  
cgs1999 写道
BigBird2012 写道
这篇文章真不错,详细全面的介绍了新的HttpClient的用法和该注意的问题。HttpClientUtil 这个工具类很好。想请教楼主一个问题,如何更好的使用HttpClient进行项目的并发性能测试呢?或者我们能交流一下使用HttpClient进行测试的方法吗?Thank楼主,期待回复!!


该方法个人觉得比较适合API开发人员自测,若要测试API的并发性能建议使用专业的测试工具,如LoadRunner、QTP。专业测试工具可以模拟指定数量的用户并发操作,并最终给出详细的测试报告。

我也建议公司采用这样的方式,但我们是小公司,领导嫌学习测试工具成本高,周期长,不愿意这样做,我也是无奈啊
2 楼 cgs1999 2014-07-15  
BigBird2012 写道
这篇文章真不错,详细全面的介绍了新的HttpClient的用法和该注意的问题。HttpClientUtil 这个工具类很好。想请教楼主一个问题,如何更好的使用HttpClient进行项目的并发性能测试呢?或者我们能交流一下使用HttpClient进行测试的方法吗?Thank楼主,期待回复!!


该方法个人觉得比较适合API开发人员自测,若要测试API的并发性能建议使用专业的测试工具,如LoadRunner、QTP。专业测试工具可以模拟指定数量的用户并发操作,并最终给出详细的测试报告。
1 楼 BigBird2012 2014-07-15  
这篇文章真不错,详细全面的介绍了新的HttpClient的用法和该注意的问题。HttpClientUtil 这个工具类很好。想请教楼主一个问题,如何更好的使用HttpClient进行项目的并发性能测试呢?或者我们能交流一下使用HttpClient进行测试的方法吗?Thank楼主,期待回复!!

相关推荐

Global site tag (gtag.js) - Google Analytics