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框架设计初衷呢

 

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