前言:
在當前開放互聯的大環境下,接口互通成了最最普通常見數據通訊與交互的一種方式;接口測試的重要性也是不言而喻,但卻成了普通功能測試人員的一道屏障;特別是服務端的接口驗證更是麻煩;這里拋磚引玉希望更多童鞋參與多多討論。
場景&描述:
c1->s1->s2
s1 <-s2
協議:http
c端,發起c1(pay)請求,服務端處理完畢,通過s1接口把(pay)請求支付接口告訴指定s2服務地址;s2服務地址解析s1接口內容,正常返回SUCCESS,異常返回FAILURE;s1收到s2返回內容做對應業務處理。
接口消息格式:json,簽名驗簽rsa標准算法,消息體url編碼。
請求方式:post
測試對象:
s1接口
測試范圍:
接口測試之常規測試維度,從宏觀到微觀一一細分拆借大概思路:消息發送方式、消息體是否為null、消息體格式是否正確、消息解密是否正確、消息體中json體key鍵檢查(必填值是否缺省、缺省值是否存在、是否多多余key鍵)、消息體中json體values值檢查(必填值檢查是否缺省、缺省值是否存在、值類型檢查,特別是數字類型的,比如交易金額啥的)。
接口測試之安全維度,ip黑白名單,消息體參數注入,摘要解密算法研究等。
1.消息發送方式
2.消息體是否為null和格式檢查
3.json,key鍵必填字檢查
4.json,values值檢查
5.業務數據檢查,比如收到SUCCESS檢查。
6.其它
實現技術分析:
如果只要驗證第5點,簡單的只要模擬s2,最快速高效的方法,直接在tomcat下,新建一個工程,創建一個html文件,內容為SUCCESS或FAILURE;然后s1接口直接往這個地址發送就好(http協議)。
http協議,后台實現,最方便的還是java,也有python的,依賴的包太多,看個人條件選擇;如果是soket套接字,建議與加密解密類語言保持一致比較好,因為解密太折騰人了,哎。
這里選擇了一個java,servlet來實現。
再回頭看s1接口,所以的參數都不是固定的,每次發生請求的參數都是動態的,這樣的話,自己去實現這個servlet的時候,參數化的部分,就直接考慮連接db,這樣就不需要考慮s1接口傳入固定的值,動態的去db查詢s1接口參數相關數據信息(特別是解密,與json,values值驗證,和功能業務特性,每次填寫固定參數值肯定不靠譜)。
-- jdbc

package com.iapppay.jdbc; import java.sql.*; import java.util.ArrayList; import java.util.List; public class mysqljdbc { public Connection conn; public Statement st; public ResultSet rs; public Connection getConn() { try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://192.168.0.151/iapppay?useUnicode=true&characterEncoding=utf-8","root","aibei1010"); } catch (Exception e) { e.printStackTrace(); } return conn; } public String getappkey(String appid,String key){ String appkey =null; //String sql = "select waresid,platpkey,platvkey,cppkey,cpvkey from wares where waresid = '3000713545';"; String sql = "select waresid,platpkey,platvkey,cppkey,cpvkey from wares where waresid = "+appid+""; conn = getConn(); try{ st = conn.createStatement(); rs = st.executeQuery(sql); while(rs.next()){ appkey = rs.getString(key); } }catch(SQLException e) { e.printStackTrace(); } return appkey; } public void insertData(String data,String rmark,String status){ int r = 0; String sql = "insert into cptest values(null,"+"'"+data+"',"+"'"+rmark+"'," +"'"+status+ "');"; conn = getConn(); try{ st = conn.createStatement(); r = st.executeUpdate(sql); if(r != '0'){ System.out.println("新增數據成功過"); }else { System.out.println("新增數據成失敗"); } }catch(SQLException e) { e.printStackTrace(); } } public String getStringData(String data) { String dataString = null; for (int i = 0;i<data.length();i++) { dataString += data.charAt(i); dataString = data.replace("\"","\\'"); } return dataString; } }
--servlet

<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>CpDataTest</servlet-name> <servlet-class> com.iapppay.service.CpDataService </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CpDataTest</servlet-name> <url-pattern>/CpDataTest</url-pattern> </servlet-mapping> </web-app>

package com.iapppay.service; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.sql.SQLException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.iapppay.jdbc.mysqljdbc; import net.sf.json.JSONObject; public class CpDataService extends HttpServlet{ private String getData(boolean i){ if (i){ StringBuffer sb = new StringBuffer(); sb.append("SUCCESS"); return sb.toString(); }else { StringBuffer sb = new StringBuffer(); sb.append("FAILURE"); return sb.toString(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub String rmark = "cp交易結果通知測試地址,請用post方式請求!"; resp.setContentType("text/html;charset=utf-8"); resp.getOutputStream().write(rmark.getBytes("utf-8")); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp){ // TODO Auto-generated method stub // super.doPost(req, resp); CpDataService cp = new CpDataService(); mysqljdbc db = new mysqljdbc(); HttpServletRequestTest reqTest = new HttpServletRequestTest(); //接受HttpServletRequest發送消息體內容 Map<String, String[]> params = req.getParameterMap(); String queryString = ""; for (String key : params.keySet()) { String[] values = params.get(key); for (int i = 0; i < values.length; i++) { String value = values[i]; queryString += key + "=" + value + "&"; } } try{ if(queryString != null && reqTest.httpReqTest(req)){ String transdata = req.getParameter("transdata"); //密鑰驗簽驗證,消息體驗證,消息體必填字驗證 String data = cp.getData(reqTest.rsaTest(req)&&reqTest.httpReqTest(req)&&reqTest.httpReqJsonTest(transdata)); // String rmark = "rsa密鑰簽名驗證結果:"+reqTest.rsaTest(req)+"\\n" +reqTest.httpReqTest(req, "test")+"\\n" +reqTest.httpReqJsonTest(transdata, "test")+"\\n"; db.insertData(queryString, rmark, data); resp.setContentType("text/html;charset=utf-8"); resp.getOutputStream().write(data.getBytes("utf-8")); }else{ String data = cp.getData(false); db.insertData(queryString, "發送消息體為NULL或transdata為NULL,密鑰驗證失敗", data); resp.setContentType("text/html;charset=utf-8"); resp.getOutputStream().write(data.getBytes("utf-8")); } } catch (Exception e) { String data = cp.getData(false); resp.setContentType("text/html;charset=utf-8"); try { resp.getOutputStream().write(data.getBytes("utf-8")); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } // public static void main(String[] args) { // // jsonAction jc = new jsonAction(); // CpDataService cp = new CpDataService(); // mysqljdbc db = new mysqljdbc(); // HttpServletRequestTest reqTest = new HttpServletRequestTest(); // String transdata = "{\"appid\":\"3000128894\",\"appuserid\":\"abc\",\"cporderid\":\"598989898986563333\",\"cpprivateinfo\":\"3333Test\",\"currency\":\"RMB\",\"notifyurl\":\"http://192.168.0.157:9888/Test/CpDataTest\",\"price\":300,\"waresid\":1,\"waresname\":\"test\"}"; // String sign = "f5eKXIS6EF3Ey2M2pAG3gJyjWRGCkkwYfHpq16X+jiQ2bbnq6wMD3g41koA9UL2SwvgYVeBpCYUeGLZITs0vVQePmpcKxsg4qqnC711bchqnfaHGDgr0QUHoYYwjfl6PPN+/hlTvHukmRNLUq/xV32Cl0QidMRj9FOWm1OxRK70="; // String signtype = "RSA"; // String dataTest = null; // JSONObject jo = JSONObject.fromObject(transdata); // String appid = jo.getString("appid"); // String data = cp.getData(jc.cpsignTest(appid, sign, transdata)); // System.out.println(reqTest.httpReqJsonTest(transdata,"Test")); // System.out.println(reqTest.httpReqJsonTest(transdata)); // db.insertData(transdata+sign+signtype, "sign解密驗簽驗證", data); // System.out.println(appid); // System.out.println(data); // } }
--jsp監控界面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.sql.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <% String path = request.getContextPath(); int recordCount=0; String stat = request.getParameter("stat") == null ? "" : request.getParameter("stat"); Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection con=java.sql.DriverManager.getConnection("jdbc:mysql://192.168.0.151/iapppay?useUnicode=true&characterEncoding=utf-8","root","aibei1010"); Statement stmt=con.createStatement(); String sql = ""; if(!stat.equals("")){ sql = "select id,data,rmark,status from cptest where status='" + stat + "' order by id desc"; }else{ sql = "select id,data,rmark,status from cptest order by id desc"; } ResultSet rst=stmt.executeQuery(sql); String sums = "select count(*) from cptest"; Statement stmt2 = con.createStatement(); ResultSet rst2 = stmt2.executeQuery(sums); if(rst2.next()){ recordCount = rst2.getInt(1); } %> <style media="screen" type="text/css"> body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; } table { font-size: 100%; } pre { } /* -- heading ---------------------------------------------------------------------- */ h1 { font-size: 16pt; color: gray; } .heading { margin-top: 0ex; margin-bottom: 1ex; } .heading .attribute { margin-top: 1ex; margin-bottom: 0; } .heading .description { margin-top: 4ex; margin-bottom: 6ex; } .overflow{ overflow:auto; } #show_detail_line { margin-top: 3ex; margin-bottom: 1ex; } </style> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>交易結果通知測試監控</title> </head> <body> <div class='heading'> <h1>愛貝交易結果通知監控</h1> <a href='index.jsp?stat=SUCCESS'>SUCCESS</a> <a href='index.jsp?stat=FAILURE'>FAILURE</a> <a href='index.jsp'>All</a> </div> <div class = 'heading'> <table border=1 style="table-layout:fixed;word-wrap:break-word;" width="100%" id = 'overflow' > <tr> <td width ="3%">ID</td> <td width ="20%">接受報體內容</td> <td width ="20%">驗證點</td> <td width ="5%">返回結果</td> </tr> <% while(rst.next()) { out.println("<tr>"); out.println("<td>"+rst.getString("id")+"</td>"); out.println("<td>"+rst.getString("data")+"</td>"); out.println("<td>"+rst.getString("rmark")+"</td>"); out.println("<td>"+rst.getString("status")+"</td>"); out.println("</tr>"); } //關閉連接、釋放資源 //<a href="http://www.w3chtml.com/" target="_blank"> //+" "+"width=50"+"height=50" %> </table> </div> </body> </html> <% rst.close(); rst2.close(); stmt.close(); stmt2.close(); con.close(); %>
--效果圖
后續:
拋磚引玉,歡迎探討~