http介面測試總結
1、使用HttpClient傳送Get/Post請求
a)Get請求
程式碼示例:
public static String getHttpResponse4Get() { CloseableHttpClient httpclient = HttpClients.createDefault(); String url = "http://www.tmall.com/test.do?id=123"; GetMethod httpGet = new GetMethod(url); httpGet.addRequestHeader("content-type", "text/html; charset=gbk"); httpGet.getParams().setParameter("http.socket.timeout", 20000); try { // 設定成了預設的恢復策略,在發生異常時候將自動重試3次,在這裡你也可以設定成自定義的恢復策略 httpGet.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler()); if (httpclient.executeMethod(httpGet) != HttpStatus.SC_OK) { System.out.println("httpGet(\"" + url + "\") failed: \n"+ httpGet.getStatusLine()); return null; } return httpGet.getResponseBodyAsString(); } catch (Exception e) { System.out.println("httpGet(\"" + url + "\") failed: \n" + e.getMessage()); return null; } finally { httpGet.releaseConnection(); httpclient = null; } }
注意點:
1、傳入引數值(或者引數串)需要進行encode,雖然httpClient中是有用URLCodec.encodeUrl()進行encode,但我實驗發現,和URLEncoder.encode()進行encode的結果還是有區別,會對最終響應有影響,所以建議自行encode;
2、需要判斷請求返回的狀態碼是否為200以後獲取到的響應才是可靠的;
3、最終要釋放掉連結,做好收尾工作。
b)Post請求
程式碼示例:
public static String getHttpResponse4Post() { try { HttpClient client = new DefaultHttpClient(); HttpPost request = new HttpPost("http://www.tmall.com/test.do"); // 使用NameValuePair來儲存要傳遞的Post引數 List<NameValuePair> postParameters = new ArrayList<NameValuePair>(); // 新增要傳遞的引數 postParameters.add(new BasicNameValuePair("id", "12345")); postParameters.add(new BasicNameValuePair("username", "dave")); // 例項化UrlEncodedFormEntity物件 UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity( postParameters); // 使用HttpPost物件來設定UrlEncodedFormEntity的Entity request.setEntity(formEntity); HttpResponse response = client.execute(request); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { String s = EntityUtils.toString(response.getEntity(), "utf-8"); System.out.println(s); System.out.println("請求正常,結束http請求"); return s; } } catch (Exception e) { System.out.println("請求發生異常,異常資訊丟擲"); e.printStackTrace(); } finally { httpClient.getConnectionManager().shutdown(); } return " "; }
c)在線上環境傳送get請求
要將在daily下執行的http介面測試指令碼放到線上執行,需要做如下一些操作:
1、建立一個真實的登入session:傳送post請求到:https://login.taobao.com/member/login.jhtml
2、然後傳送get請求到http://i.taobao.com/my_taobao.htm從快取中同步一次cookie
3、通過this.httpClientLogin.getState().getCookies();獲取到cookie資訊,然後解析到_tb_token_的值
4、把這個_tb_token_這個引數和值附加到測試請求的url中,通過get方式進行請求即可
5、注意:在登入的post引數設定時,需要將字符集設為:GBK
postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "GBK");
如果改為utf-8或者不set此param就可能導致獲取的cookie值不足的問題
(本部分感謝浣碧同學的悉心指點!)
2、urlencode/urldecode
為什麼url需要encode以後才能傳送,是因為URL需要轉化為ASCII字符集才能在被HTTP協議使用。ASCII字符集7位的字符集,包含128個字元,其中常見的有數字1-9,還有a-zA-Z以及一些特殊字元。當URL中包含非ASCII字符集的字元的時候,就需要encode為ASCII字符集,以便通過HTTP協議傳送請求
Java中進行URL Encode和Decode的方法是:
URLEncoder.encode(valueOfUrlParam, "UTF-8");
URLDecoder.decode(urlParamStr, "UTF-8");
兩個類都來自java.net的包,平時可以使用online的encode/decode網站進行操作
這裡需要注意的是:
只能對URL的引數進行encode(可以把整個引數串進行encode,也可以只對引數值進行encode,不能對域名和URI進行encode,例如:
http://www.tmall.com/test.do?id=123,234,正確的encode以後的url為:
http://www.tmall.com/test.do?id%3d123%2c234 或者:
http://www.tmall.com/test.do?id=123%2c234
在用post發請求的時候,需要將引數內容組裝成HttpEntity然後set到post物件中,此時會用到UrlEncodedFormEntity這個類,這類的建構函式裡會把post的引數進行一次encode,所以自己不需要額外進行一次encode,否則應用伺服器在一次decode情況下就會讀取錯誤的引數值
3、mock
a) 引數mock
問題場景:在服務端應用程式裡通常會有這樣的片段:
public static Long getLoginUserId(TurbineRunData rundata) {
String userId = (String) rundata.getRequest().getSession()
.getAttribute(SessionKeeper.ATTRIBUTE_USER_ID_NUM);
if (StringUtils.isNumeric(userId)) {
return Long.parseLong(userId);
}
return null;
}
if (userId==null || userId <= 0) {
result.setSuccess(false);
result.setErrorCode(MallBrandResultCode.USER_NOT_LOGIN);
result.setErrorMsg("引數user_id缺失");
}
如果webx不能從session中獲取到userID,那就會被業務邏輯控制住,不能繼續往下走,解決辦法有兩個:
l.往session裡塞userId
要實現這種方案,首先需要建立一個真實daily下使用者登入的session,由於是在daily下,還需要解決安全認證的問題,這樣便能解決這個問題。但明顯這樣做的成本太高,而且不是很必要。
l通過get引數mock掉這段程式碼邏輯
和開發約定一個引數和引數值,在程式中判斷如果有這個引數值則直接返回一個指定的userId。
b)壓縮mock
為了防止前端傳送的json資料時http的get請求超長,前後端開發通過7zip進行壓縮,但httpClient認為這樣的壓縮過的請求是BadRequest,所以只能試圖把請求換成post,同時把解壓縮的邏輯mock掉,以便於daily下的介面測試。具體詳見:《http介面測試中有關URL長度限制的問題(Maximum URL Length)》
c)csrf控制mock
程式碼中為了防止csrf攻擊,都會加入csrf的控制模組,在daily下可以直接讓開發把這段邏輯註釋掉,但如果指令碼還要在系統上線後迴歸,那就需要和開發一起把這段邏輯mock掉,以簡化測試應用的複雜度,降低測試指令碼開發難度。