接口测试可以分为两部分:
一是线上接口(生产环境)自动化测试,需要自动定时执行,每5分钟自动执行一次,相当于每5分钟就检查一遍线上的接口是否正常,有异常能够及时发现,不至于影响用户使用。
二是测试环境的接口自动化测试,测试时机可以是1、功能开发完成并提测,检查一边测试环境的所有接口是否正常,有异常则需要解决后再正式介入测试。2、测试完成上线发版前,检查一边测试环境的所有接口是否正常,有异常则需要解决后再上线发版。
jmeter是Apache的一个用java写成,用来做接口测试和压力测试的工具,有gui界面,因为是纯java的,所以可以跨平台使用(只要有java环境就可以),jmeter已经把做接口测试的各种方法封装的很完善稳定,所以我们可以直接用来做接口自动化测试,不需要自己再去封装各种请求、断言、结果处理等等方法。相当于把jmeter的脚本当作接口测试用例管理工具,在脚本里面把要测试的接口、断言都写好。
基于jmeter做接口自动化框架包括以下几个部分:
jmeter+jenkins+github+钉钉机器人+tomcat+shell脚本+java程序
包含了这些功能后,几乎大部分都可以自动化,手工需要做的事情就只有第三步:写接口用例并推送到github
步骤大致是这样:用jmeter写接口—推送到GitHub—jenkins定时自动执行测试(从GitHub拉取最新脚本—执行shell脚本—执行java代码,处理统计结果,发送报告)
以下是框架具体步骤:
一、多人协作就需要github来管理,在github新建仓库,用于存放jmx脚本
二、每个测试人员需要下载sourcetree,在本地新建本地仓库,并关联github远程仓库
三、将各自写好的jmx脚本放到本地仓库,提交并推送到github
四、在服务器上部署jenkins
1、配置git源代码管理,如果远程仓库是私有类型的,则需要配置证书或账号密码(每次构建测试时,jenkins都会检查远程仓库是否有更新脚本,有更新就拉最新的脚本来测试,这样也方便修改被测脚本)
2、设置定时自动构建,下面是每天的8点到23点,每5分钟构建一次
3、设置超时管理,为了不影响下一次构建
4、配置要执行的shell脚本
5、配置构建失败通知钉钉
五、在服务器配置tomcat环境(如果是win服务器,最好用服务的形式安装tomcat)
------linux配置tomcat环境-----
1、下载Core下的zip包,解压到linux的目录
2、到bin目录下给catalina.sh和startup.sh赋予chmod 777权限,或者给全部sh文件赋予最高权限chmod 777 *.sh
3、sh startup.sh启动,在浏览器中输入http://10.10.10.115:8080/出现tomcat页面表示启动成功
注意:如果访问不成功,如果我们想在本机中检测是否已经成功部署,那么我们必须关掉Linux中的防火墙或者开放8080端口,我在这里采用开放端口的方法。
4、用指令sh shutdown.sh停止tomcat的运行,继续进行后续操作
5、查看防火墙是否开启:systemctl status firewalld
未开启:Active: inactive (dead)
已开启:Active: active (running)
在CentOS 7或RHEL 7或Fedora中防火墙由firewalld来管理:
开启防火墙:systemctl start firewalld
关闭防火墙:systemctl stop firewalld
执行开机禁用防火墙自启命令 : systemctl disable firewalld.service
开放端口号:firewall-cmd --permanent --zone=public --add-port=8080/tcp(只能在防火墙开启的情况下开放端口)
6、将要查看的文件放到webapps/ROOT/re.html这里,浏览器直接访问http://10.10.10.115:8080/re.html就可以看到了
------windows配置tomcat环境-----
1、下载安装程序
2、双击exe文件安装-选择jre目录-选择tomcat安装目录-选择端口(默认8080可修改)
在tomcat/bin目录双击startup.bat启动服务
或者
双击Tomcat9w.exe-start
3、不用配置环境变量,但是需要关闭win防火墙
https://jingyan.baidu.com/article/17bd8e52083ce685ab2bb839.html(win关闭防火墙的方法)
六、在服务器配置jmeter环境
需要两个jmeter环境,jmeter1用来收集jtl结果和生成报告,不需要改参数。jmeter2需要收集xml日志,把jmeter/bin/user.properties文件在结尾新增参数
jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.response_data=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.responseHeaders=true
七、shell脚本,包括以下几个步骤
删除上次的测试结果
在tomcat目录中新建index文件夹,用于存放本次测试结果
挨个执行jmx脚本(每个脚本需要执行两次,第一次收集jtl结果和生成报告,第二次收集xml日志,将报告、xml日志文件放到tomcat的目录下)
执行java程序
八、java程序(框架核心),把写好的java代码打成一个可执行jar包,让shell来调用,需要包含以下功能
1、用时间戳重命名index文件夹,保证每次的结果目录不会覆盖
2、把xml日志文件cope到index下面
3、统计fail接口数量
4、收集fail接口的信息
5、将信息写成html页面报告,并放到tomcat目录下,同时用同一个时间戳生成报告的url
6、将测试结果的简要信息、报告的url、日志的url,调用钉钉机器人,发送到钉钉群,并且艾特相关接口负责人,以下是相关代码
依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
代码:
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
class DingDingTools {
private String WEBHOOK_TOKEN;
DingDingTools(String token) {
WEBHOOK_TOKEN = token;
}
private void send(String textMsg) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(WEBHOOK_TOKEN);
httpPost.addHeader("Content-Type", "application/json; charset=utf-8");
StringEntity se = new StringEntity(textMsg, "utf-8");
httpPost.setEntity(se);
CloseableHttpResponse response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String result = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("发送钉钉消息成功 " + result);
} else {
System.out.println("发送钉钉消息失败");
}
// 关闭
httpClient.close();
response.close();
}
//发送text格式的消息
void text(String mag, String[] atMobile, boolean isAtAll) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_text = new JSONObject();
JSONObject object_at = new JSONObject();
object_text.put("content",mag);
object_at.put("atMobiles",atMobile);
object.put("msgtype","text");
object.put("text",object_text);
object.put("at",object_at);
object.put("isAtAll",isAtAll);
send(String.valueOf(object));
}
//发送link格式的消息
void link(String title, String text, String messageUrl, String picUrl) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_link = new JSONObject();
object_link.put("text", text);
object_link.put("title", title);
object_link.put("picUrl", picUrl);
object_link.put("messageUrl", messageUrl);
object.put("msgtype", "link");
object.put("link", object_link);
send(String.valueOf(object));
}
//发送markdown格式的消息
void markdown(String title, String text, String atMobile, boolean isAtAll) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_markdown = new JSONObject();
JSONObject object_at = new JSONObject();
object_markdown.put("title", title);
object_markdown.put("text", text);
object_at.put("atMobiles", atMobile);
object_at.put("isAtAll", isAtAll);
object.put("msgtype", "markdown");
object.put("markdown", object_markdown);
object.put("at", object_at);
send(String.valueOf(object));
}
//整体跳转ActionCard类型
void actionCard(String title, String text, String singleTitle, String singleURL, int btnOrientation, int hideAvatar) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_actionCard = new JSONObject();
object_actionCard.put("title",title);
object_actionCard.put("text",text);
object_actionCard.put("hideAvatar",hideAvatar);
object_actionCard.put("btnOrientation",btnOrientation);
object_actionCard.put("singleTitle",singleTitle);
object_actionCard.put("singleURL",singleURL);
object.put("actionCard",object_actionCard);
object.put("msgtype","actionCard");
send(String.valueOf(object));
}
//独立跳转ActionCard类型
void actionCard1(String title, String text, String btnsTitle1, String btnsTitle2, String actionURL1, String actionURL2, int btnOrientation, int hideAvatar) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_actionCard1 = new JSONObject();
JSONObject object_btns1 = new JSONObject();
JSONObject object_btns2 = new JSONObject();
object_btns1.put("title",btnsTitle1);
object_btns1.put("actionURL",actionURL1);
object_btns2.put("title",btnsTitle2);
object_btns2.put("actionURL",actionURL2);
object_actionCard1.put("title",title);
object_actionCard1.put("text",text);
object_actionCard1.put("hideAvatar",hideAvatar);
object_actionCard1.put("btnOrientation",btnOrientation);
object_actionCard1.put("btns","["+object_btns1+","+object_btns2+"]");
object.put("actionCard",object_actionCard1);
object.put("msgtype","actionCard");
send(String.valueOf(object));
}
//FeedCard类型
void feedCard(String title1, String messageURL1, String picURL1, String title2, String messageURL2, String picURL2) throws IOException {
JSONObject object = new JSONObject();
JSONObject object_feedCard = new JSONObject();
JSONObject object_link1 = new JSONObject();
JSONObject object_link2 = new JSONObject();
object_link1.put("title",title1);
object_link1.put("messageURL",messageURL1);
object_link1.put("picURL",picURL1);
object_link2.put("title",title2);
object_link2.put("messageURL",messageURL2);
object_link2.put("picURL",picURL2);
object_feedCard.put("links","["+object_link1+","+object_link2+"]");
object.put("feedCard",object_feedCard);
object.put("msgtype","feedCard");
send(String.valueOf(object));
}
}
7、将测试报告发邮件给相关人员,以下是相关代码
依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.5</version>
</dependency>
代码:
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.MultiPartEmail;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class CommonsMail {
public static void mail(String[] to, String[] cc, String[] path) throws EmailException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");// 定义时间格式
String time = sdf.format(new Date());// 获取当前时间
MultiPartEmail multiPartEmail = new MultiPartEmail();
multiPartEmail.setHostName("smtp.qiye.163.com");//邮件服务器地址
multiPartEmail.setAuthentication(Constants.sender, Constants.senderPassword);//发送邮件服务器账户密码
multiPartEmail.setCharset("UTF-8");//发送邮件的字符编码
//可不加
multiPartEmail.setSSLOnConnect(false);//开启SSL加密,可不加
multiPartEmail.setStartTLSEnabled(false);//开启TLS加密,可不加
multiPartEmail.setSmtpPort(25);//端口可以不设置,默认25
multiPartEmail.setDebug(false);//调试模式
//发送邮件的地址
multiPartEmail.setFrom(Constants.sender);
multiPartEmail.setSubject("线上接口自动化测试报告");//主题
multiPartEmail.setMsg("附件为 "+time+" 线上接口测试报告");//正文
for (String toList : to) {
if (toList != null && toList.trim().length() > 0) {
multiPartEmail.addTo(toList);//接收邮件地址
}
}
for (String ccList : cc) {
if (ccList != null && ccList.trim().length() > 0) {
multiPartEmail.addCc(ccList);//抄送人地址
}
}
for (String pathList : path) {
if (pathList != null && pathList.trim().length() > 0) {
EmailAttachment attachment = new EmailAttachment();//创建附件的对象
attachment.setPath(pathList);//设置附件内容
attachment.setName("");// 邮件发送出去时附件名
multiPartEmail.attach(attachment);//添加附件
}
}
multiPartEmail.send();
System.out.println("邮件发送成功!\n" + "收件人:\n" + Arrays.toString(Constants.TOLIST1) + "\n" + "抄送人:\n" + Arrays.toString(Constants.CCLIST1));
}
}
///钉钉通知消息效果
测试报告的url页面
日志url页面
点击全部接口跳转的url报告(fail的接口需要标红)