zoukankan      html  css  js  c++  java
  • 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();

    /*
    设定group,只运行其中的几个group,只设置group名称是无法运行的,必须先加载用例,
    可以是class也可以是xml,另外根据源码可知group名称是可以添加多个的,以,分隔即可;
    如果只是某些group不想运行,则可以用方法testNG.setExcludedGroups(groups);
    同理这个groups同样是可以包含一个或者多个group名称,以,分隔;
    TestNG testNG1 = new TestNG();
    testNG1.setVerbose(3);
    Class[] classes1 = {Config.class,Demand.class};
    testNG1.setTestClasses(classes1);
    testNG1.setGroups("group1");
    testNG1.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框架设计初衷呢
  • 相关阅读:
    oracle提交commit后回退恢复
    jQuery toggle() 方法 : 切换隐藏和显示
    sql server vs mysql
    Redis学习
    【转发】c#做端口转发程序支持正向连接和反向链接
    IKVM
    注册表
    解决VS2010自带的C/C++编译器CL找不到mspdb100.dll的问题
    【转载】Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration info
    Openwrt路由器上开发微信公众号应用
  • 原文地址:https://www.cnblogs.com/Chilam007/p/15614483.html
Copyright © 2011-2022 走看看