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框架设计初衷呢
  • 相关阅读:
    程序员的7中武器
    需要强化的知识
    微软中国联合小i推出MSN群Beta 不需任何插件
    XML Notepad 2006 v2.0
    Sandcastle August 2006 Community Technology Preview
    [推荐] TechNet 广播 SQL Server 2000完结篇
    《太空帝国 4》(Space Empires IV)以及 xxMod 英文版 中文版 TDM Mod 英文版 中文版
    IronPython 1.0 RC2 更新 1.0.60816
    Microsoft .NET Framework 3.0 RC1
    《Oracle Developer Suite 10g》(Oracle Developer Suite 10g)V10.1.2.0.2
  • 原文地址:https://www.cnblogs.com/Chilam007/p/15614483.html
Copyright © 2011-2022 走看看