接口測試可以分爲兩部分:
一是線上接口(生產環境)自動化測試,需要自動定時執行,每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來管理:https://blog.csdn.net/ViJayThresh/article/details/81284007
開啓防火牆: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的接口需要標紅)