jetty模擬服務端作為測試樁進行介面測試詳細介紹
有時,在進行介面測試時,很多時候需要依賴外部的介面環境,但在實際開發中,尤其是敏捷開發模式中,很多時候依賴的外部介面環境可能不通或者未開發完畢,這時候無法及時進行端到端的測試,測試樁的必要性就非常重要了。
但由於SoapUI通常部署在本地的Windows環境上(linux環境本人未使用過,不建議),而測試環境通常部署在linux伺服器上,可能存在測試環境無法調通本地環境的情況,這時就需要另一種方式部署到linux伺服器進行介面測試,詳細如下:
一、測試樁專案介
紹
1. jetty介紹:
Jetty 是一個開源的servlet容器,它為基於Java的web容器,易用性是
Jetty 設計的基本原則,詳情可百度之
所需jar包(本人):
2. 測試樁目的:
測試人員在測試中,尤其是進行介面測試時,經常需要使用到測試樁來進行測試,通常情況下,相應的開發人員會寫好相應的測試樁,以jar包的形式作為一個服務端給客戶端進行呼叫(當介面聯調未能按計劃進行或延遲時,測試人員應主導向開發人員要求提供測試樁進行測試,具體視實際情況而定)
3. 測試樁原理
相當於啟動一個jetty容器,攔截對應的請求,返回相應的報文。
4. 測試樁使用過程
1.在server包下寫一個帶main函式(這個main函式會啟動一個jetty容器)的java類,配置埠號,想要攔截的請求,和對應的處理請求的servlet
<span style="font-size:18px;">package cn.migu.server; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import cn.migu.servlet.JsonResponseDemo; import cn.migu.servlet.XmlResponseDemo; import cn.migu.util.Log4jUtil; /** * <Description> 服務入口配置 * @author YanLu * */ public class HttpServerDemo { //private static Log4jUtil log = new Log4jUtil(HttpServerDemo.class.getName()); /** * main方法入口 * * @param args */ public static void main(String[] args) { try { Server server = new Server(19993); // 指定服務的埠號 ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); // 一個context就是一個WEB-Application context.setContextPath("/test"); // 訪問專案名(路徑) server.setHandler(context); // servlet對映的路徑,類似於web.xml的servlet url-pattern定義 context.addServlet(new ServletHolder(new XmlResponseDemo()), "/ChannelFaqSearch"); // 兩個引數分別為攔截請求的servlet和想要攔截的路徑 context.addServlet(new ServletHolder(new JsonResponseDemo()), "/ExecuteCampaign"); //log.info("server start."); System.out.println("server start."); // 啟動服務 server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } } </span>
2.在servlet中處理請求,返回報文(測試樁主要目的是模擬介面,返回報文。請求處理啥的就掠過啦~)
<1> 處理xml格式響應報文,coding如下:
<span style="font-size:18px;">package cn.migu.servlet; import java.io.IOException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.migu.util.ConfigUtil; import cn.migu.util.GlobalSettings; import cn.migu.util.Log4jUtil; import cn.migu.util.OutPrinterUtil; import cn.migu.util.XMLReaderHelper; /** * <Description> 攔截執行活動請求,返回xml格式報文 * @author YanLu * */ public class XmlResponseDemo extends HttpServlet { //private Log4jUtil log = new Log4jUtil(this.getClass().getName()); @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { StringBuilder builder; //String filePath = "/apps/stub_test/responseProfile/ChannelFaqSearch.xml"; //String filePath = GlobalSettings.getProperty("ChannelFaqSearch"); //讀取外部的xml檔案 String filePath = ConfigUtil.CONFIG.get("ChannelFaqSearch"); //將xml檔案轉化為String String xmlStr = XMLReaderHelper.xmlStrReader(filePath); if (!(null == xmlStr)) { System.out.println("讀取XML成功!"); builder = new StringBuilder(xmlStr); } else { System.out.println("讀取XML失敗!"); builder = new StringBuilder(1024); bindBuilder(builder); } //響應xml格式字串 OutPrinterUtil.outputXml(response, builder); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { //同doPost方法 doPost(request, response); } private void bindBuilder(StringBuilder builder) { builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?><notifyRsp>"); builder.append("<transId>" + "cs112233" + "</transId>"); builder.append("<processTime>" + "230ms" + "</processTime>"); builder.append("<cpId>710302</cpId>"); builder.append("<bulletinType>1</bulletinType>"); builder.append("<bulletinID>1</bulletinID>"); builder.append("<bulletinID>1</bulletinID>"); builder.append("<returnCode>0000</returnCode>"); builder.append("<bulletinTitle>停電公告</bulletinTitle>"); builder.append("<bulletinCont>十月31號停電</bulletinCont>"); builder.append("<publishTime>20151021101212</publishTime>"); builder.append("<publishType>0</publishType>"); builder.append("<bulletinLevel>0</bulletinLevel>"); builder.append("<contactType>1</contactType>"); builder.append("<needReply>1</needReply>"); builder.append("<adminName> 管理員 </adminName >"); builder.append("<bulletinAttachs>"); builder.append("<bulletinAttach>"); builder.append("<attachName> 附件1 </attachName >"); builder.append("<attachType> 1 </attachType >"); builder.append("<attachURL> ftp://192.168.1.1/test.doc</attachURL>"); builder.append("</bulletinAttach>"); builder.append("<bulletinAttach>"); builder.append("<attachName> 附件2 </attachName >"); builder.append("<attachType> 2 </attachType >"); builder.append("<attachURL > ftp://192.168.1.1/test2.doc</attachURL>"); builder.append("</bulletinAttach >"); builder.append("</bulletinAttachs >"); builder.append("<replys >"); builder.append("<reply >"); builder.append("<replyTime > 20151011121212 </replyTime >"); builder.append("<replyType > 1 </replyType >"); builder.append("<replyCont > CP回覆測試1 </replyCont >"); builder.append("<replyAttchs >"); builder.append("<replyAttch >"); builder.append("<attachName > 附件1 </attachName >"); builder.append("<attachURL > ftp://192.168.1.1/test.doc</attachURL>"); builder.append("</replyAttch >"); builder.append("</replyAttchs >"); builder.append("</reply >"); builder.append("<reply >"); builder.append("<replyTime > 20151011121212 </replyTime >"); builder.append("<replyType > 2 </replyType >"); builder.append("<replyCont > 管理員回覆測試1 </replyCont >"); builder.append("<replyAttchs >"); builder.append("<replyAttch >"); builder.append("<attachName > 附件1 </attachName >"); builder.append("<attachURL > ftp://192.168.1.1/test.doc</attachURL>"); builder.append("</replyAttch >"); builder.append("</replyAttchs >"); builder.append("</reply >"); builder.append("</replys >"); builder.append("</notifyRsp>"); } } </span>
<2> 處理json格式響應報文,coding如下:
<span style="font-size:18px;">package cn.migu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.migu.util.ConfigUtil;
import cn.migu.util.FileUtil;
import cn.migu.util.GlobalSettings;
import cn.migu.util.OutPrinterUtil;
import java.io.IOException;
/**
* <Description> 攔截執行活動請求,返回json格式報文
* @author YanLu
*
*/
public class JsonResponseDemo extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//String filePath = "/apps/stub_test/responseProfile/ExecuteCampaign.json";
String filePath = ConfigUtil.CONFIG.get("ExecuteCampaign");
//將json檔案轉化為String
String resultJson= FileUtil.ReadFile(filePath);
//響應json格式字串
OutPrinterUtil.outputJson(resultJson, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}
</span>
重要說明:
<1> 檔案存放位置如下:
<span style="font-size:18px;">#配置檔案路徑
#檔案置於當前工程的profile目錄下
#ChannelFaqSearch=./profile/ChannelFaqSearch.xml
#ExecuteCampaign=./profile/ExecuteCampaign.json
#檔案置於與專案同級的目錄下
ChannelFaqSearch=./responseProfile/ChannelFaqSearch.xml
ExecuteCampaign=./responseProfile/ExecuteCampaign.json
#檔案路徑為按絕對路徑
#ChannelFaqSearch=/apps/responseProfile/ChannelFaqSearch.xml</span>
<2> xml檔案轉化為String的程式碼如下:
<span style="font-size:18px;">package cn.migu.util;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* <Description> XMLReader幫助類
* @author YanLu
*
*/
public class XMLReaderHelper {
private static Document document;
//將xml檔案轉換為String,使用dom方式解析xml
public static String xmlStrReader(String fileName) {
try {
//獲取DOM解析器工廠,以便生產解析器
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//獲取DOM解析器,以便解析DOM
DocumentBuilder db = dbf.newDocumentBuilder();
//把要解析的 XML 文件轉化為輸入流,以便 DOM 解析器解析它
//InputStream is= new FileInputStream("test1.xml");
//解析 XML 文件的輸入流,得到一個 Document
document = db.parse(fileName);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
t.transform(new DOMSource(document), new StreamResult(bos));
String xmlStr = bos.toString();
return xmlStr;
} catch (ParserConfigurationException e) {
System.err.println(e.getMessage());
} catch (SAXException e) {
System.err.println(e.getMessage());
} catch (IOException e) {
System.err.println(e.getMessage());
} catch (TransformerConfigurationException e) {
System.err.println(e.getMessage());
} catch (TransformerException e) {
System.err.println(e.getMessage());
}
return null;
}
}
</span>
<3> json檔案轉換String程式碼如下:
<span style="font-size:18px;">package cn.migu.util;
import java.io.*;
/**
* <Description> 讀取檔案方法封裝
* @author YanLu
*
*/
public class FileUtil {
/**
* 讀取.json檔案方法
* @param filePath
* @return
*/
public static String ReadFile(String filePath) {
BufferedReader reader=null;
StringBuilder result=null;
try {
FileInputStream inStream=new FileInputStream(filePath);
InputStreamReader inReader=new InputStreamReader(inStream,"UTF-8");
reader=new BufferedReader(inReader);
result=new StringBuilder();
String tempStr;
while((tempStr=reader.readLine())!=null){
result.append(tempStr);
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(reader!=null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result.toString();
}
}
</span>
<4> 輸出響應報文程式碼如下:
<span style="font-size:18px;">package cn.migu.util;
import org.eclipse.jetty.server.Request;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
/**
* <Description> 響應輸出流工具類示例,可自行定製
*
* @author YanLu
*
*/
public class OutPrinterUtil {
/**
* 響應xml格式字串
*
* @param response
* @param result
* @throws IOException
*/
public static void outputXml(HttpServletResponse response, StringBuilder result) throws IOException {
// response.setContentType("application/xml");
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
System.err.println("響應碼為:"+HttpServletResponse.SC_OK);
PrintWriter pw = response.getWriter();
pw.print(result.toString());
pw.flush();
pw.close();
}
/**
* 響應json格式字串
*
* @param json
* @param response
*/
public static void outputJson(String json, HttpServletResponse response) {
try {
response.setCharacterEncoding("UTF-8");
// json串必須要是json格式,否則會出錯
response.setContentType("application/json");
//在代理伺服器端防止緩衝
response.setDateHeader("Expires", 0);
PrintWriter out = response.getWriter();
out.print(json);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 需要返回給使用者的結果不支援session
*
* @param baseRequest
* @param response
* @param result
* @throws IOException
*/
public static void outputNoSession(Request baseRequest, HttpServletResponse response, String result)
throws IOException {
response.setContentType("text/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println(result);
}
/*
* private OutPrinterUtil() { super(); }
*/
}
</span>
3.測試樁執行。以HttpServerDemo為例,啟動HttpServerDemo.java的main方法,瀏覽器中輸入main方法中配置的請求路徑http://localhost:19993/test/ChannelFaqSearch(我本機跑,ip為localhost,佈置到伺服器,換成伺服器ip),就可以看到返回的報文了。
4.注意點
在伺服器上打包執行,打包時注意main函式的選取(選自己寫的要啟動的埠對應的java類)。
以eclipse打jar包為例,步驟如下:
右鍵選中測試樁工程 --> export -->
點選next
點選finish完成
然後將打好的jar包(如jettyServer.jar)上傳至伺服器
即可命令執行jar包(先給jar檔案賦許可權,否者可能無法執行)。
例:命令如下:java -jar jettyServer.jar
二、測試樁伺服器部署
1 部署測試樁環境
1>.測試樁完成後打為jar包,需確認包中定義的相關埠號,及測試樁的請求路徑。
2>.在配置檔案中修改介面地址為對應的測試樁的地址(ip為jar包所在服務的主機ip,埠為定義的埠)
3>.將jar包部署到伺服器(我目前在192.168.129.145伺服器上建立了一個目錄作為測試樁的存放路徑:/apps/stub_test/大家可以統一將jar包放置此處,當然也可以自定義)
2 啟動關閉測試樁
Linux上啟動測試樁命令:
1>.#java -jar testStub.jar &
//該命令表示啟動jar包
//注:加&表示將程序放置在後臺執行
2>.#jobs
//檢視後臺執行的任務列表
3>.#nohup java -jar testStub.jar &
//如果想在退出終端後服務不終止則使用nohup命令,nohup的作用是即使退出終端,程式仍不會結束,使用nohup命令應注意該服務不會停止,不用時應注意將該程序kill 掉
//若出現下面提示資訊,不是報錯,它表示程式執行資訊會輸出到nohup.out
#nohup: ignoring input and appending output to `nohup.out'
4>.#jobs -l
如圖所示,顯示該服務的程序號
#kill -9 程序號
//幹掉程序
3 注意事項
1>.在使用測試樁時,返回報文是作為一個xml(或json)檔案放在外部進行讀取的,這樣便於測試在測試時可通過修改xml檔案的形式進行更多場景的測試
2>.如果在啟動jar包時報錯,注意是否是埠號被佔用
#知識傳送門:
以上是我寫的用於測試樁的mock平臺,非常簡單易懂,直接將專案打成war包部署tomcat容器即可執行。
該平臺通過前臺web頁面管理介面,可作為輕量級的mock平臺