jetty模拟服务端作为测试桩进行接口测试详细介绍

来源:互联网 发布:淘宝清理缓存后会怎样 编辑:程序博客网 时间:2024/05/22 13:37

有时,在进行接口测试时,很多时候需要依赖外部的接口环境,但在实际开发中,尤其是敏捷开发模式中,很多时候依赖的外部接口环境可能不通或者未开发完毕,这时候无法及时进行端到端的测试,测试桩的必要性就非常重要了。

我在上一篇文章中已介绍使用第三方工具SoapUI做为模拟服务端作为测试桩进行接口测试,详见http://blog.csdn.net/russ44/article/details/52230355
但由于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-Applicationcontext.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文件转化为StringString 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.xmlExecuteCampaign=./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.javamain方法,浏览器中输入main方法中配置的请求路径http://localhost:19993/test/ChannelFaqSearch(我本机跑,iplocalhost,布置到服务器,换成服务器ip),就可以看到返回的报文了。


4.注意点

在服务器上打包运行,打包时注意main函数的选取(选自己写的要启动的端口对应的java类)。

以eclipsejar包为例,步骤如下:

右键选中测试桩工程 --> export -->



点击next

 


点击finish完成

然后将打好的jar包(如jettyServer.jar)上传至服务器

即可命令运行jar(先给jar文件赋权限,否者可能无法执行)。

例:命令如下:java -jar jettyServer.jar

 

二、测试桩服务器部署

1 部署测试桩环境

1>.测试桩完成后打为jar包,需确认包中定义的相关端口号,及测试桩的请求路径。

2>.在配置文件中修改接口地址为对应的测试桩的地址(ipjar包所在服务的主机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包时报错,注意是否是端口号被占用


知识传送门:

https://git.oschina.net/russ44/easyTest

以上是我写的用于测试桩的平台,直接将项目部署tomcat容器即可运行。通过前台页面管理接口,分享给大家使用

0 0