Java接口自动化测试框架设计-6-日志输出和常用代码提取成方法

       前面说过了,如果你稍微懂得TestNG这个单元测试框架,到目前这个简单的Java接口自动化测试框架主体的骨架部分已经完成设计并实现。这篇,继前篇的基础上,把测试用例中获取响应状态码和响应数据转换成JSON格式这些经常重复的代码,给提取出来,构造成方法来调用。然后就是给这个框架添加一个日志输出功能,方便得到运行结果和运行出错的情况下的debug。

1.添加log输出支持

1.1 maven依赖引入

      这里,我试过apache log4j.jar 和slf4j.jar,由于log4j在maven项目上不能自动识别log4j.properties这个资源文件,最后我还是选择了maven引入slf4j.jar,在maven 配置文件pom.xml添加如下依赖,然后保存。

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.7.2</version>
</dependency>

1.2 新建src/main/config资源包

      在Eclipse上点击当前项目名,右键new -source folder,输出src/main/config,点击确定,然后在src/main/config下新建一个log4j.properties文件,内容如下。

### set log levels ###
log4j.rootLogger = INFO, stdout, file

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss SSS} %-5p %c{1}:%L - %m%n

log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = ./log/apilog.log
# overwirte the old log file
log4j.appender.file.Append = false      
## 
log4j.appender.file.Threshold = INFO
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss SSS} %-5p %c{1}:%L - %m%n

1.3 在项目根目录下新建log文件夹

     新建这个文件夹是上面log4j.properties文件我们设置的日志保存文件路径是在./log文件夹下。大致的项目结构图如下

2.优化RestClient.java内容

      主要是把在测试断言中经常需要拿到的响应状态码和json解析之前需要把响应内容转换成json格式,这部分内容给提取到方法里。然后我们写上log输出。(完整代码请看文件结尾处百度云链接)

2.1.RestClient类添加日志输出和代码提取成方法

package com.qa.restclient;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
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 org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class RestClient {
	
	final static Logger Log = Logger.getLogger(RestClient.class);
	
	/**
	 * 不带请求头的get方法封装
	 * @param url
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse get (String url) throws ClientProtocolException, IOException {
		
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpGet的请求对象
		HttpGet httpget = new HttpGet(url);
		//执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
		Log.info("开始发送get请求...");
		CloseableHttpResponse httpResponse = httpclient.execute(httpget);
		Log.info("发送请求成功!开始得到响应对象。");
		return httpResponse;
	}
	
	/**
	 * 带请求头信息的get方法
	 * @param url
	 * @param headermap,键值对形式
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse get (String url,HashMap<String,String> headermap) throws ClientProtocolException, IOException {
			
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpGet的请求对象
		HttpGet httpget = new HttpGet(url);
		//加载请求头到httpget对象
		for(Map.Entry<String, String> entry : headermap.entrySet()) {
			httpget.addHeader(entry.getKey(), entry.getValue());
		}
		//执行请求,相当于postman上点击发送按钮,然后赋值给HttpResponse对象接收
		CloseableHttpResponse httpResponse = httpclient.execute(httpget);
		Log.info("开始发送带请求头的get请求...");	
		return httpResponse;
	}
	
	/**
	 * 封装post方法
	 * @param url
	 * @param entityString,其实就是设置请求json参数
	 * @param headermap,带请求头
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse post (String url, String entityString, HashMap<String,String> headermap) throws ClientProtocolException, IOException {
		//创建一个可关闭的HttpClient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();
		//创建一个HttpPost的请求对象
		HttpPost httppost = new HttpPost(url);
		//设置payload
		httppost.setEntity(new StringEntity(entityString));
		
		//加载请求头到httppost对象
		for(Map.Entry<String, String> entry : headermap.entrySet()) {
			httppost.addHeader(entry.getKey(), entry.getValue());
		}
		//发送post请求
		CloseableHttpResponse httpResponse = httpclient.execute(httppost);
		Log.info("开始发送post请求");
		return httpResponse;
	}
	
	/**
	 * 封装 put请求方法,参数和post方法一样
	 * @param url
	 * @param entityString,这个主要是设置payload,一般来说就是json串
	 * @param headerMap,带请求的头信息,格式是键值对,所以这里使用hashmap
	 * @return 返回响应对象
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse put (String url, String entityString, HashMap<String,String> headerMap) throws ClientProtocolException, IOException {
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPut httpput = new HttpPut(url);
		httpput.setEntity(new StringEntity(entityString));
	
		for(Map.Entry<String, String> entry : headerMap.entrySet()) {
			httpput.addHeader(entry.getKey(), entry.getValue());
		}
		//发送put请求
		CloseableHttpResponse httpResponse = httpclient.execute(httpput);
		return httpResponse;
	}
	
	/**
	 * 封装 delete请求方法,参数和get方法一样
	 * @param url, 接口url完整地址
	 * @return,返回一个response对象,方便进行得到状态码和json解析动作
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public CloseableHttpResponse delete (String url) throws ClientProtocolException, IOException {
			
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpDelete httpdel = new HttpDelete(url);
		
		//发送delete请求
		CloseableHttpResponse httpResponse = httpclient.execute(httpdel);
		return httpResponse;
	}
	
	/**
	 * 获取响应状态码,常用来和TestBase中定义的状态码常量去测试断言使用
	 * @param response
	 * @return 返回int类型状态码
	 */
	public int getStatusCode (CloseableHttpResponse response) {
		
		int statusCode = response.getStatusLine().getStatusCode();
		Log.info("解析,得到响应状态码:"+ statusCode);
		return statusCode;
		
	}
	
	/**
	 * 
	 * @param response, 任何请求返回返回的响应对象
	 * @return, 返回响应体的json格式对象,方便接下来对JSON对象内容解析
	 * 接下来,一般会继续调用TestUtil类下的json解析方法得到某一个json对象的值
	 * @throws ParseException
	 * @throws IOException
	 */
	public JSONObject getResponseJson (CloseableHttpResponse response) throws ParseException, IOException {
		Log.info("得到响应对象的String格式");
		String responseString = EntityUtils.toString(response.getEntity(),"UTF-8");
		JSONObject responseJson = JSON.parseObject(responseString);
		Log.info("返回响应内容的JSON格式");
		return responseJson;
	}
}

2.2 TestNG测试用例添加日志输出

package com.qa.tests;

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.alibaba.fastjson.JSONObject;
import com.qa.base.TestBase;
import com.qa.restclient.RestClient;
import com.qa.util.TestUtil;

public class GetApiTest extends TestBase {
	
	TestBase testBase;
	String host;
	String url;
	RestClient restClient;
	CloseableHttpResponse closeableHttpResponse;
	
	final static Logger Log = Logger.getLogger(GetApiTest.class);
	
	@BeforeClass
	public void setUp() {
		
		testBase = new TestBase();
		//Log.info("测试服务器地址为:"+ host.toString());
		host = prop.getProperty("HOST");
		//Log.info("当前测试接口的完整地址为:"+url.toString());
		url = host + "/api/users?page=2";
		
	}
	
	@Test
	public void getAPITest() throws ClientProtocolException, IOException {
		Log.info("开始执行用例...");
		restClient = new RestClient();
		closeableHttpResponse = restClient.get(url);
		
		//断言状态码是不是200
		Log.info("测试响应状态码是否是200");
		int statusCode = restClient.getStatusCode(closeableHttpResponse);
		Assert.assertEquals(statusCode, RESPNSE_STATUS_CODE_200, "response status code is not 200");
		
        JSONObject responseJson = restClient.getResponseJson(closeableHttpResponse);
        //System.out.println("respon json from API-->" + responseJson); 
        
        //json内容解析
        String s = TestUtil.getValueByJPath(responseJson,"data[0]/first_name");
        Log.info("执行JSON解析,解析的内容是 " + s);
        //System.out.println(s);
        Log.info("接口内容响应断言");
        Assert.assertEquals(s, "Eve","first name is not Eve");
        Log.info("用例执行结束...");
	}
	
	
}

     这里,我强调下,我在上面BeforeClass部分无法引入Log输出,上面代码我注销了日志打印,如果不注销,这块会报空指针异常,很奇怪,只有在@BeforeClass中报错,在@Test中没有,我花了一些时间,还是搞不懂这块,所以,就放弃在beforeclass部分添加日志输出。

看看在./log/api.log的日志输出效果

2018-05-26 17:39:21 864 INFO  TestBase:28 - 正在读取配置文件...
2018-05-26 17:39:21 926 INFO  TestBase:28 - 正在读取配置文件...
2018-05-26 17:39:21 943 INFO  GetApiTest:39 - 开始执行用例...
2018-05-26 17:39:22 463 INFO  RestClient:41 - 开始发送get请求...
2018-05-26 17:39:23 206 INFO  RestClient:43 - 发送请求成功!开始得到响应对象。
2018-05-26 17:39:23 206 INFO  GetApiTest:44 - 测试响应状态码是否是200
2018-05-26 17:39:23 207 INFO  RestClient:146 - 解析,得到响应状态码:200
2018-05-26 17:39:23 209 INFO  RestClient:160 - 得到响应对象的String格式
2018-05-26 17:39:23 297 INFO  RestClient:163 - 返回响应内容的JSON格式
2018-05-26 17:39:23 299 INFO  GetApiTest:53 - 执行JSON解析,解析的内容是 Eve
2018-05-26 17:39:23 299 INFO  GetApiTest:55 - 接口内容响应断言
2018-05-26 17:39:23 300 INFO  GetApiTest:57 - 用例执行结束...

说明:

      这个Java接口自动化测试框架,更适合于接口的单元测试。也就是说,写接口测试用例的人员必须会Java,必须掌握TestNG的基本使用。而且所有的接口测试用例都是以一个个不同TestNG类文件组成。所以,这个框架无法帮助黑盒测试人员完成接口自动化测试,只适合会写单元测试的自动化测试人员,依赖单元测试框架去管理和运行接口测试用例,拿到测试报告。当然,后续可以扩展支持Jenkins持续集成和测试。

      由于个人在大型项目接口自动化方面经验欠缺,只能完成目前这个简单的接口自动化测试框架。所以,这个接口自动化测试框架设计系列文章就先到这里结束。最后,贴出整个项目的完整源码,百度云链接,点击这里



猜你喜欢

转载自blog.csdn.net/u011541946/article/details/80463228
今日推荐