测试框架HttpClient
HTTPClient简介
? HTTPClient和浏览器有点像,但却不是浏览器。很多人觉得既然HttpClient是一个HTTP客户端编程工具,很多人把他当做浏览器来理解,但是其实HttpClient不是浏览器,它是一个HTTP通信库,因此它只提供一个通用浏览器应用程序所期望的功能子集,最根本的区别是HttpClient中没有用户界面,浏览器需要一个渲染引擎来显示页面,并解释用户输入,例如鼠标点击显示页面上的某处,有一个布局引擎,计算如何显示HTML页面,包括级联样式表和图像。javascript解释器运行嵌入HTML页面或从HTML页面引用的javascript代码。来自用户界面的事件被传递到javascript解释器进行处理。除此之外,还有用于插件的接口,可以处理Applet,嵌入式媒体对象(如pdf文件,Quicktime电影和Flash动画)或ActiveX控件(可以执行任何操作)。HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。
HttpClient的主要功能:
- 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
- 支持 HTTPS 协议
- 支持代理服务器(Nginx等)等
- 支持自动(跳转)转向
- …
HttpClient使用
我们的第一个demo
-
首先我们创建一个
maven 项目,然后在pom.xml 中添加以下HTTPClient 依赖<dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency> </dependencies>
-
创建java文件,编写测试代码,进行模拟一个请求,获取百度的请求响应结果
package com.pgytesting.httpclient.demo01; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.testng.annotations.Test; import java.io.IOException; /** * @version v1.0 * @ProjectName: AutoTest * @ClassName: MyHttpClientDemo01 * @Description: TODO(HTTPClient的第一个demo) * @User: sun * @Author: [email protected] * @Date: 2019/6/4 15:36 */ public class MyHttpClientDemo01 { /** * 这里我们说明一个事情,因为Apache官网对HTTPClient进行更新, * 从4.3开始已经对旧版本的 DefaultHttpClient 和 HttpResponse 方法进行了更新,不在推荐使用 * 我们可以使用 : * DefaultHttpClient --> CloseableHttpClient * HttpResponse --> CloseableHttpResponse * 具体的demo可以在 CloseableHttpClientAndCloseableHttpResponse 类中查看详情 * */ @Test public void test1() throws IOException { // result用来存放我们的请求结果 String result; // 创建一个httpcl 用来请求 CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet get = new HttpGet("http://www.baidu.com"); // 使用HTTPClient请求get,并使用CloseableHttpResponse接收请求的结果 CloseableHttpResponse response = httpClient.execute(get); // 我们对请求进行获取Entity,然后使用EntityUtils进行格式化为string字符串,防止字符编码问题,也传给他一个utf-8参数 result = EntityUtils.toString(response.getEntity(),"utf-8"); System.out.println(result); } }
-
我们的版本迭代方法更新演示demo
package com.pgytesting.httpclient.demo01; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; 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.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.util.ArrayList; import java.util.List; /** * @version v1.0 * @ProjectName: AutoTest * @ClassName: CloseableHttpClientAndCloseableHttpResponse * @Description: TODO(一句话描述该类的功能) * @User: sun * @Author: [email protected] * @Date: 2019/6/4 15:52 */ public class CloseableHttpClientAndCloseableHttpResponse { @Test public void get() throws IOException { /** * 官方示例get请求 */ CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet("http://targethost/homepage"); CloseableHttpResponse response1 = httpclient.execute(httpGet); try { System.out.println(response1.getStatusLine()); HttpEntity entity1 = response1.getEntity(); // do something useful with the response body // and ensure it is fully consumed EntityUtils.consume(entity1); } finally { response1.close(); } /** * 官方示例post请求 */ HttpPost httpPost = new HttpPost("http://targethost/login"); //拼接参数 List<NameValuePair> nvps = new ArrayList<>(); nvps.add(new BasicNameValuePair("username", "vip")); nvps.add(new BasicNameValuePair("password", "secret")); httpPost.setEntity(new UrlEncodedFormEntity(nvps)); CloseableHttpResponse response2 = httpclient.execute(httpPost); try { System.out.println(response2.getStatusLine()); HttpEntity entity2 = response2.getEntity(); // do something useful with the response body // and ensure it is fully consumed //消耗掉response EntityUtils.consume(entity2); } finally { response2.close(); } } }
-
运行结果
[TestNG] Running: C:Usersfrosty.IntelliJIdea2019.1system emp-testng-customsuite.xml <!DOCTYPE html> <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html> =============================================== Default Suite Total tests run: 1, Failures: 0, Skips: 0 ===============================================
HTTPClient Get方法实现并携带Cookie
首先我们先使用之前的moco进行请求的创建
-
修改之前我们创建的cookie请求的json文件,吧请求和响应都创建好
[ { "description": "这是一个会返回cookie信息的get请求", "request": { "uri": "/getCookies", "method": "get" }, "response": { "cookies": { "login": "true" }, "text": "恭喜你获取Cookies信息成功" } }, { "description": "这是一个带cookies信息的get请求", "request": { "uri": "/getDemo/Cookies", "method": "get", "cookies": { "login": "true" } }, "response": { "text": "这个是一个必须带cookies信息才可以访问的get请求", "headers": { "Content-Type": "text/html;charset=gbk" } } }, { "description": "这是一个带cookies信息的post请求", "request": { "uri": "/postDemo/Cookies", "method": "post", "cookies": { "login": "true" }, "json": { "name": "胡汉三", "sex": "man" } }, "response": { "status": 200, "json": { "code": "success", "msg": "胡汉三,请求成功了" } } } ]
-
启动我们的moco,然后在我们的浏览器中测试刚才我们创建的请求,此处如果使用火狐,安全策略可能导致我们的cookie缓存失败,建议直接使用Google浏览器
其次我们在编写代码进行测试
-
创建我们的
urlproperties.properties 文件## 我们测试用的url test.url=http://localhost:8888 getCookies.url=/getCookies
-
编写java代码
package com.pgytesting.httpclient.demo02; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; import java.util.Locale; import java.util.ResourceBundle; /** * @version v1.0 * @ProjectName: AutoTest * @ClassName: MyCookiesForGet * @Description: TODO(使用HTTPClient的get请求我们的cookies) * @User: sun * @Author: [email protected] * @Date: 2019/6/4 17:04 */ public class MyCookiesForGet { /** * 定义变量url,用来存储请求地址 */ private String url; /** * 定义读取properties文件的对象 */ private ResourceBundle bundle; /** * 创建HTTPClient请求对象 */ private CloseableHttpClient httpClient = HttpClients.createDefault(); /** * 在我们测试之前就把配置问价那种的url参数读取出来 */ @BeforeTest public void beforeTest() { // 可以用此方法读取到我们的properties文件,会默认识别后缀,不用写后缀,直接写文件名称就OK // 并且要在读取时进行编码传递,直接 Locale.CHINA 即可 bundle = ResourceBundle.getBundle("urlproperties", Locale.CHINA); // 直接写入要获取配置文件中的的key值即可 url = bundle.getString("test.url"); } @Test public void testGetCookies() throws IOException { String result; // 进行我们的测试url拼接 String testUrl = this.url + bundle.getString("getCookies.url"); // 进行URL测试 HttpGet get = new HttpGet(testUrl); HttpResponse response = httpClient.execute(get); result = EntityUtils.toString(response.getEntity(),"utf-8"); // 对我们的结果进行打印 System.out.println(result); } }
-
执行测试,查看运行结果
[TestNG] Running: C:Usersfrosty.IntelliJIdea2019.1system emp-testng-customsuite.xml 恭喜你获取Cookies信息成功 =============================================== Default Suite Total tests run: 1, Failures: 0, Skips: 0 ===============================================
然后我们对之前的方法进行改良
-
增加可以获取cookie的方法
package com.pgytesting.httpclient.demo02; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; /** * @version v1.0 * @projectName: AutoTest * @className: MyCookiesForGet * @description: TODO(使用HTTPClient的get请求我们的cookies) * @author : [email protected] * @date : 2019/6/4 17:04 */ public class MyCookiesForGet { /** * 定义变量url,用来存储请求地址 */ private String url; /** * 定义读取properties文件的对象 */ private ResourceBundle bundle; /** * 创建CookieStore对象 */ private CookieStore cookieStore; /** * 创建HTTPClient请求对象 */ private CloseableHttpClient httpClient = HttpClients.createDefault(); /** * 获取cookie的方法 */ private HttpClientContext httpClientContext = HttpClientContext.create(); /** * 在我们测试之前就把配置文件中的url参数读取出来 */ @BeforeTest public void beforeTest() { // 可以用此方法读取到我们的properties文件,会默认识别后缀,不用写后缀,直接写文件名称就OK // 并且要在读取时进行编码传递,直接 Locale.CHINA 即可 bundle = ResourceBundle.getBundle("application", Locale.CHINA); // 直接写入要获取配置文件中的的key值即可 url = bundle.getString("test_url"); } /** * 此方法对mock中我们模拟的下发cookie的请求(/getCookies)进行访问获取cookie参数 * * @throws IOException 抛出IO流异常 */ @Test public void testGetCookies() throws IOException { String result; // 进行我们的测试url拼接 String testUrl = this.url + bundle.getString("getCookies_url"); // 把url封装进httpGet对象中 HttpGet get = new HttpGet(testUrl); // 使用HTTPClient对象进行请求,此处需要注意,cookie信息通过httpClientContext对象获取到cookies相关信息 HttpResponse response = this.httpClient.execute(get, httpClientContext); // 对响应参数进行格式化为String字符串 result = EntityUtils.toString(response.getEntity(), "utf-8"); // 对我们的结果进行打印 System.out.println(result); // 获取cookie信息 cookieStore = httpClientContext.getCookieStore(); // 获取cookie对象list数组进行迭代输出 List<Cookie> listCookies = httpClientContext.getCookieStore().getCookies(); listCookies.forEach(cookie -> { System.out.println("cookie的name = " + cookie.getName()); System.out.println("cookie的value = " + cookie.getValue()); }); } /** * dependsOnMethods 此参数设置我们的这个测试方法依赖testGetCookies()方法 * * 在依赖测试方法执行完成后,获取到对应的cookie参数,进行次测试方法执行,对cookie进行校验 * * @throws IOException 抛出IO流异常 */ @Test(dependsOnMethods = {"testGetCookies"}) public void testGetWithCookies() throws IOException { String testUrl = this.url + bundle.getString("test_get_with_cookie"); // 进行http的get请求封装 HttpGet httpGet = new HttpGet(testUrl); // 利用httpClientContext把cookie对象添加进去 httpClientContext.setCookieStore(cookieStore); // 利用HTTPClient对象进行get请求 HttpResponse response = this.httpClient.execute(httpGet, httpClientContext); // 获取响应的状态码 int coude = response.getStatusLine().getStatusCode(); System.out.println("响应状态码为:" + coude); // 我们对状态码进行判断,只有在成功的时候进行响应参数打印 if (coude == 200) { // 对响应的参数进行格式化为String String result = EntityUtils.toString(response.getEntity(), "utf-8"); System.out.println(result); } } /** * 我们定义方法,在测试执行结束,把其请求关闭,防止 mock 抛出异常 * * @throws IOException 抛出IO流异常 */ @AfterTest public void afterTest() throws IOException { // 需要关闭请求流通道,否则会在程序执行结束强制结束会话,导致mock抛出异常信息 httpClient.close(); } }
-
添加请求的url地址到配置文件application.properties
## 我们测试用的url ## 测试的请求地址 test_url=http://localhost:8888 ## 测试请求获取cookie的url getCookies_url=/getCookies ## 测试必须用cookie才能访问的地址 test_get_with_cookie=/getDemo/Cookies
-
然后我们执行代码,得到执行结果,请求成功
[TestNG] Running: C:Usersfrosty.IntelliJIdea2019.1system emp-testng-customsuite.xml 恭喜你获取Cookies信息成功 cookie的name = login cookie的value = true 响应状态码为:200 这个是一个必须带cookies信息才可以访问的get请求 =============================================== Default Suite Total tests run: 2, Failures: 0, Skips: 0 ===============================================
HTTPClient 实现Post方法并携带Cookie
-
首先我们后面要使用到json请求的参数,要涉及到json我们必须引入json的依赖包
<dependencies> <!-- https://mvnrepository.com/artifact/org.json/json --> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20180813</version> </dependency> </dependencies>
-
添加请求路径
test_post_with_cookie=/postDemo/Cookies 到application.properties配置文件中## post请求的请求路径 test_post_with_cookie=/postDemo/Cookies
-
然后我们编写java代码,我们继续使用get请求中的代码来获取cookie信息,然后发送post请求携带cookie信息和json参数去验证
package com.pgytesting.httpclient.demo02; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.cookie.Cookie; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.ResponseContent; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONObject; import org.testng.Assert; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; /** * @author : [email protected] * @version : v1.0 * @projectName : AutoTest * @className : MyCookiesForPost * @description : TODO(使用HTTPClient的post请求我们的cookies) * @date : 2019/6/5 8:54 */ public class MyCookiesForPost { /** * 定义变量url,用来存储请求地址 */ private String url; /** * 定义读取properties文件的对象 */ private ResourceBundle bundle; /** * 创建CookieStore对象 */ private CookieStore cookieStore; /** * 创建HTTPClient请求对象 */ private CloseableHttpClient httpClient = HttpClients.createDefault(); /** * 获取cookie的方法 */ private HttpClientContext httpClientContext = HttpClientContext.create(); /** * 在我们测试之前就把配置文件中的url参数读取出来 */ @BeforeTest public void beforeTest() { // 可以用此方法读取到我们的properties文件,会默认识别后缀,不用写后缀,直接写文件名称就OK // 并且要在读取时进行编码传递,直接 Locale.CHINA 即可 bundle = ResourceBundle.getBundle("application", Locale.CHINA); // 直接写入要获取配置文件中的的key值即可 url = bundle.getString("test_url"); } /** * 此方法对mock中我们模拟的下发cookie的请求(/getCookies)进行访问获取cookie参数 * * @throws IOException 抛出IO流异常 */ @Test public void testGetCookies() throws IOException { String result; // 进行我们的测试url拼接 String testUrl = this.url + bundle.getString("getCookies_url"); // 把url封装进httpGet对象中 HttpGet get = new HttpGet(testUrl); // 使用HTTPClient对象进行请求,此处需要注意,cookie信息通过httpClientContext对象获取到cookies相关信息 HttpResponse response = this.httpClient.execute(get, httpClientContext); // 对响应参数进行格式化为String字符串 result = EntityUtils.toString(response.getEntity(), "utf-8"); // 对我们的结果进行打印 System.out.println(result); // 获取cookie信息 cookieStore = httpClientContext.getCookieStore(); // 获取cookie对象list数组进行迭代输出 List<Cookie> listCookies = httpClientContext.getCookieStore().getCookies(); listCookies.forEach(cookie -> { System.out.println("cookie的name = " + cookie.getName()); System.out.println("cookie的value = " + cookie.getValue()); }); } /** * dependsOnMethods 此参数设置我们的这个测试方法依赖testGetCookies()方法 * <p> * 在依赖测试方法执行完成后,获取到对应的cookie参数,进行次测试方法执行,对cookie进行校验 * * @throws IOException 抛出IO流异常 */ @Test(dependsOnMethods = {"testGetCookies"}) public void testPostMethod() throws IOException { // 拼接post请求的请求地址 String testUrl = this.url + bundle.getString("test_post_with_cookie"); // 创建一个post请求 HttpPost httpPost = new HttpPost(testUrl); // 创建json对象,存储请求数据 JSONObject jsonObject = new JSONObject(); jsonObject.put("name", "胡汉三"); jsonObject.put("sex", "man"); // 设置请求头信息 设置header信息 此处的编码和下面代码中的编码不是同一个东西, // 一个是设置发送请求的参数编码,一个是设置一什么编码参数 httpPost.setHeader("Content-Type", "application/json;charset=utf8"); // 创建一个StringEntity对象把请求参数进行封装,并设置请求编码 StringEntity stringEntity = new StringEntity(jsonObject.toString(), "utf-8"); // 把post请求参数添加进post请求中 httpPost.setEntity(stringEntity); // 利用httpClientContext把cookie对象添加进去 httpClientContext.setCookieStore(cookieStore); // 使用HTTPClient进行进行post请求,并使用httpResponse对象接收响应参数 HttpResponse httpResponse = httpClient.execute(httpPost, httpClientContext); // 获取到请求状态码 int status = httpResponse.getStatusLine().getStatusCode(); // 对响应状态码进行验证,如果为成功状态,则打印响应参数 if (status == 20000) { System.out.println("请求成功了"); // 将参数获取到并转换为String字符串 String result = EntityUtils.toString(httpResponse.getEntity(), "gbk"); // 将字符串参数转换为json对象 JSONObject resultJson = new JSONObject(result); // 打印响应的json数据 System.out.println(resultJson); // 对请求结果进行判断,获取到期望结果和实际结果进行判断 String code = String.valueOf(resultJson.get("code")); String msg = String.valueOf(resultJson.get("msg")); // 结果进行校验,对期望和实际进行判断 Assert.assertEquals("success", code); Assert.assertEquals("胡汉三,请求成功了", msg); System.out.println("测试通过了"); } } /** * 我们定义方法,在测试执行结束,把其请求关闭,防止 mock 抛出异常 * * @throws IOException 抛出IO流异常 */ @AfterTest public void afterTest() throws IOException { // 需要关闭请求流通道,否则会在程序执行结束强制结束会话,导致mock抛出异常信息 httpClient.close(); } }
-
运行代码查看我们的运行结果
- 在请求结果中我们发现我们在post请求中可以自定义返回的http响应状态码
[TestNG] Running: C:Usersfrosty.IntelliJIdea2019.1system emp-testng-customsuite.xml 恭喜你获取Cookies信息成功 cookie的name = login cookie的value = true 请求成功了 {"msg":"胡汉三,请求成功了","code":"success"} 测试通过了 =============================================== Default Suite Total tests run: 2, Failures: 0, Skips: 0 ===============================================