TestNG+HttpClient —— 第一個項目

一、前言

  前面幾篇講了testng和httpclient的基本使用,掌握這些知識後足夠可以開展新項目了,因爲只有在項目中才會遇到各種新問題,纔會推動自己去學習更多的東西。本篇主要會以貼代碼的形式去講述自己做的項目,不會有太多的文字描述了。

  以前用httprunner做過一個項目,本篇的項目 用例設計原理同httprunner(感興趣的可以去翻翻我之前的博文),大概就是把公共的東西抽出來(比如登錄 、環境),然後一個菜單一個腳本文件,文件裏可以包含多個用例。

  本篇涉及到的知識:

  • HttpClient的post、get、put請求封裝
  • java的實例、成員變量、局部變量
  • jsonpath提取響應字段(用的是阿里的fastjson)
  • 正則表達式提取響應字段
  • testng運行

  項目用例的主線流程:登錄 > 新建訂單  > 錄入資料後發貨 

二、項目結構展示

 

 三、項目詳細示例

1、common包下面的HttpClientUtils.java

  說明:封裝了httpclient請求,以及業務系統的環境地址

  用處:業務系統可調用封裝好的代碼直接發送httpclient請求,減少代碼冗餘

package com.tech.common;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
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;

/**
 * @author 一加一
 * @date 2021-11-18
 * HttpClient 工具類
 */

public class HttpClientUtils {
    /**
     * 定義host,環境地址
     * @return
     */
    public static String  host(){
        String host = "https://xxx-api.xxx.cn";
        return host;
    }

    /**
     * get請求
     * @param url
     * @param token
     * @return
     */
    public static String doGet(String url,String token) throws IOException{
        try {
            //創建瀏覽器對象
            HttpClient httpClient = HttpClients.createDefault();
            //創建get請求對象
            HttpGet httpGet = new HttpGet(url);
            //添加get請求頭
            httpGet.setHeader("Accept","application/json");
            httpGet.setHeader("Content-Type","application/json");
            httpGet.setHeader("Token",token);
            //執行get請求
            HttpResponse response = httpClient.execute(httpGet);
            //請求發送成功,並得到響應
            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                //讀取服務器返回的json字符串
                String jsonString = EntityUtils.toString(response.getEntity());
                //將json字符串return
                return jsonString;
            }
        }
        catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * post請求(用於請求json格式的參數)
     * @param url
     * @param params
     * @param token
     * @return
     */
    public static String doPost(String url,String params,String token) throws IOException{
        
        //創建瀏覽器對象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //創建httpPost對象
        HttpPost httpPost = new HttpPost(url);
        //添加httpPost的請求頭
        httpPost.setHeader("Accept","application/json");
        httpPost.setHeader("Content-Type","application/json");
        httpPost.setHeader("Token",token);
        //將請求參數加入到Entity
        StringEntity entity = new StringEntity(params,"UTF-8");
        httpPost.setEntity(entity);
        //定義響應變量,初始值爲null
        CloseableHttpResponse response = null;

        try {
            //執行請求並用變量接收返回的響應值
            response = httpClient.execute(httpPost);
            //獲取響應的狀態
            StatusLine status = response.getStatusLine();
            //獲取響應的code
            int state = status.getStatusCode();
            //SC_OK=200
            if(state == HttpStatus.SC_OK){
                HttpEntity responseEntity = response.getEntity();
                String jsonString = EntityUtils.toString(responseEntity);
                return jsonString;
            }
            else {
                System.out.println(("返回錯誤"));
            }
        }
        finally {
            if(response!=null){
                try {
                    response.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            try {
                httpClient.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * put請求(用於請求json格式的參數)
     * @param url
     * @param params
     * @param token
     * @return
     */
    public static String doPut(String url,String params,String token) throws IOException{

        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPut httpPut = new HttpPut(url);
        httpPut.setHeader("Accept","application/json");
        httpPut.setHeader("Content-Type","application/json");
        httpPut.setHeader("Token",token);
        StringEntity entity = new StringEntity(params,"UTF-8");
        httpPut.setEntity(entity);
        CloseableHttpResponse response = null;

        try {
            response = httpClient.execute(httpPut);
            StatusLine status = response.getStatusLine();
            int state = status.getStatusCode();
            if(state == HttpStatus.SC_OK){
                HttpEntity responseEntity = response.getEntity();
                String jsonString = EntityUtils.toString(responseEntity);
                return jsonString;
            }
            else {
                System.out.println(("返回錯誤"));
            }
        }
        finally {
            if(response!=null){
                try {
                    response.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            try {
                httpClient.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        return null;
    }
}

 2、testng包下面的Config.java

  說明:登錄系統A的用例

  用處:【登錄】,主要爲了獲取登錄成功後動態生成的token,業務系統的接口請求時需要用到動態的token

package com.tech.testng;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Config {
    //定義類的成員變量
    String code = "";//生成token接口需要用到code
    String Token="";
    String host = "https://qa-xxx-api.xxx.cn";//登錄環境域名(登錄系統跟業務系統是2個不同的系統,所以這裏又重新定義了個host)
    String loginFromServer = "https://xxx.cn";//指向對應系統環境域名
    String username = "李白";//登錄賬號
    String password = "123456";//登錄密碼
    
    @Test(description ="登錄接口")
    public void Login() throws IOException {
        String url = host+"/saas/auth/login";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36");
        httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

        List<BasicNameValuePair> param = new ArrayList<>(4);
        param.add(new BasicNameValuePair("loginFromServer", loginFromServer));
        param.add(new BasicNameValuePair("username", username));
        param.add(new BasicNameValuePair("password", password));

        UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(param, StandardCharsets.UTF_8);
        httpPost.setEntity(formEntity);
        CloseableHttpResponse response = httpClient.execute(httpPost);//執行請求
        //System.out.println("響應狀態爲:" + response.getStatusLine());
        String body = EntityUtils.toString(response.getEntity());//獲取響應內容
        System.out.println("Login的響應內容爲:" + body);

        JSONObject jsonObject = JSON.parseObject(body);//轉換成json格式
        String data = jsonObject.getString("data");//獲取到data值       //拼接雙引號: "\""+    +"\""

        Pattern pattern = Pattern.compile("code=(.*)");//正則表達式提取code
        Matcher matcher = pattern.matcher(data);

        if(matcher.find()) {
            code = matcher.group(1);
            System.out.println("提取的code爲:" + code);
        }
    }

    @Test(description = "生成token接口",dependsOnMethods = {"Login"})
    public String exchangeToken() throws IOException {
        String url = host+"/saas/auth/exchangeToken";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36");
        httpPost.addHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");

        List<BasicNameValuePair> param = new ArrayList<>(1);
        param.add(new BasicNameValuePair("code",code));
        UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(param, StandardCharsets.UTF_8);
        httpPost.setEntity(formEntity);//執行請求

        CloseableHttpResponse response = httpClient.execute(httpPost);//獲取響應內容
        HttpEntity res = response.getEntity();
        String message = EntityUtils.toString(res);
        //String body = EntityUtils.toString(response.getEntity());
        //System.out.println("exchangeToken的響應內容爲:"+body);
        System.out.println("token響應"+message);

        JSONObject jsonObject = JSON.parseObject(message);//轉換成json格式
        JSONObject jsonObject1 = jsonObject.getJSONObject("data");//獲取到data值
        Token = jsonObject1.getString("ssoToken");//獲取data對象裏的ssoToken,並賦值給Token
        System.out.println("exchangeToken生成的Token爲:"+Token);

        //返回Token供其他用例引用
        return Token;
    }

}

3、demand包下面的Demand.java

  說明:業務系統B的用例

  用處:【新建訂單】

package com.tech.demand;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tech.common.HttpClientUtils;
import com.tech.testng.Config;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;

public class Demand {
    //定義全局變量
    String token ="";
    String styleCode = "";

    //調用類的靜態成員方法
    String host = HttpClientUtils.host();

    @BeforeTest
    public void Token() throws IOException {
        //如何得到類的對象:類名  對象名 = new 類名()  得到Config類的對象
        Config config = new Config();
        config.Login();
        //如何使用對象:1、訪問屬性:對象名.成員變量      2、訪問行爲:對象名.方法名(...)
        token = config.exchangeToken();//調用Config類的方法,用全局變量token接收方法返回的Token值
        System.out.println(token);
    }

    @Test(description = "新建訂單")
    public void putOrder() throws IOException {
        String url = host+"/order/web/v1/create";
        //新建訂單
        String params = "{"Id":"68163112","Name":"短袖"}";
        //直接調用封裝好的請求方法
        String body = HttpClientUtils.doPut(url,params,token);
        System.out.println("新建訂單成功"+body);
    }

    @Test(description = "訂單列表查詢",dependsOnMethods = {"putOrder"})
    public String postPage() throws  IOException {
        String url = host+"/web/v1/order/page";
        String params = "{"pageNum":1,"pageSize":20}";
        String body = HttpClientUtils.doPost(url,params,token);

        //jsonpath提取響應字段
        JSONObject jsonObject = JSON.parseObject(body);
        JSONObject data = jsonObject.getJSONObject("data");//得到data的json對象
        JSONArray list = data.getJSONArray("list");//得到data中的list對象,並用list保存
        JSONObject jsonObject1 = list.getJSONObject(0);//獲取list下標爲0的數據
        styleCode = jsonObject1.getString("styleCode");//提取styleCode字段
        System.out.println(styleCode);
        return styleCode;
    }
}

 4、demand包下面的Production.java

  說明:業務系統B的用例

  用處:【錄入資料後發貨】

package com.tech.demand;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tech.common.HttpClientUtils;
import com.tech.testng.Config;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;

public class Production {
    //定義全局變量
    String token ="";
    String styleId ="";
    String demandDetailId ="";
    String orderId ="";
    //調用類的靜態成員方法
    String host = HttpClientUtils.host();

    @BeforeTest
    public void Token() throws IOException {
        //如何得到類的對象:類名  對象名 = new 類名()  得到Config類的對象
        Config config = new Config();
        config.Login();
        //如何使用對象:1、訪問屬性:對象名.成員變量      2、訪問行爲:對象名.方法名(...)
        token = config.exchangeToken();//調用Config類的方法,用全局變量ssotoken接收方法返回的Token值
        System.out.println(token);
    }

    @Test(description = "發貨列表查詢")
    public void getPage() throws IOException {
        String url = host+"/web/v1/info/page?pageNum=1&pageSize=20";
        String body = HttpClientUtils.doGet(url,token);
        System.out.println("發貨列表查詢成功,響應內容爲:"+body);

        JSONObject jsonObject = JSON.parseObject(body);
        JSONObject data = jsonObject.getJSONObject("data");//得到data的json對象
        JSONArray list = data.getJSONArray("list");//得到data中的list對象,並用list保存
        JSONObject jsonObject1 = list.getJSONObject(0);//獲取list下標爲0的數據
        styleId = jsonObject1.getString("styleId");
        demandDetailId = jsonObject1.getString("demandDetailId");
    }

    @Test(description = "發貨-附件資料上傳",dependsOnMethods = {"getPage"})
    public void postAdd() throws IOException{
        String url = host+"/web/v1/info/add";
        //報價單【附件類型(3-報價單,4-商品單)】
        String params3 = "{"attachmentType":"3","attachmentUrl":"702074.jpeg"}";
        //商品單
        String params4 =  "{"attachmentType":"4","attachmentUrl":"902034.jpeg"}";
        String body = HttpClientUtils.doPost(url,params3,token);
        String body1 = HttpClientUtils.doPost(url,params4,token);
        System.out.println("報價單上傳成功"+body);
        System.out.println("商品單上傳成功"+body1);
    }

    @Test(description = "獲取發貨詳情",dependsOnMethods = {"postAdd"})
    public void getDetail() throws IOException{
        String url = host+"/web/v1/production-order/detail/detail-id?demandDetailId="+demandDetailId;
        String body = HttpClientUtils.doGet(url,token);

        JSONObject jsonObject = JSON.parseObject(body);
        JSONObject data = jsonObject.getJSONObject("data");//得到data的json對象
        orderId = data.getString("orderId");//提取orderId
    }
    @Test(description = "發貨資料提交",dependsOnMethods = {"getDetail"},enabled = true)
    public void postSubmit() throws IOException{
        String url = host+"/web/v1/order/submit/"+styleId;
        String params = "{}";
        String body = HttpClientUtils.doPost(url,params,token);
        System.out.println("發貨成功,響應爲:"+body);
    }
}

5、執行TestNG

1)方式一:編程代碼方式執行

  在testng包下面新建test.java,代碼如下:

package com.tech.testng;

import com.tech.demand.*;import org.testng.TestNG;

import java.io.IOException;

public class test {

    public static void main(String[] args) throws IOException {

        //在main函數調用
        TestNG testNG = new TestNG();
        Class[] classes = {Demand.class, Production.class};
        testNG.setTestClasses(classes);
        testNG.run();
    }
}

2)方式二:XML方式執行

  在HelloTest-src目錄下新建testng.xml,代碼如下:

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >

<suite name="Suite1" verbose="1" >
    <test name="test1" >
        <classes>
            <class name="com.tech.demand.Demand"></class>
            <class name="com.tech.demand.Production"></class>
        </classes>
    </test>

</suite>

6、執行結果

  忽略,這裏不貼圖了

四、總結

  以上就是整個項目的代碼了,當然業務系統的用例只貼出了一部分,但並不影響演示項目實例。

  因爲剛開始用testng做,先羅列下該項目存在的問題,看看後面有沒有辦法可以優化:

  • 現狀1:業務系統的.java用例文件,每次執行前都要先實例登錄
  • ——>存在的問題:倘若.java用例文件有20個,那執行完一個項目則需要重複登錄20次
  • 現狀2:爲了讓業務系統的每個.java用例文件都可以獨立運行,目前的處理方式是先列表查詢出最新的數據去處理
  • ——>存在的問題:倘若剛好有人同時創建了一條數據,那該用例查出來的最新數據就不是自己上一步創建的,而是別人的,至此會導致後面用例可能出現問題
  • 現狀3:一個.java用例文件,可能包含很多@Test,因爲是主流程自動化,入參要用到前面接口的出參,所以採用了dependsOnMethods用例依賴
  • ——>存在的問題:不太明白這種方式是否符合“用例要獨立可運行”的原則,因爲如果依賴用例失敗的話,該用例就會失敗
  • 現狀4:目前個人用的是編程代碼方式去執行用例
  • ——>存在的問題:看了testng官方文檔,以及查了很多網上資料,都寫的是用xml方式去執行,是否個人使用的不合testng框架設計初衷呢

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章