前面说过了,如果你稍微懂得TestNG这个单元测试框架,到目前这个简单的Java新浪股票交易接口api,接口自动化测试框架主体的骨架部分已经完成设计并实现。这篇,继前篇的基础上,把测试用例中获取响应状态码和响应数据转换成JSON格式这些经常重复的代码,给提取出来,构造成方法来调用。然后就是给这个框架添加一个日志输出功能,方便得到运行结果和运行出错的情况下的debug。
添加log输出支持
maven依赖引入
这里,我试过apachelog4jar和slf4jar,由于log4j在maven项目上不能自动识别log4properties这个资源文件,最后我还是选择了maven引入slf4jar,在maven配置文件poxml添加如下依赖,然后保存。
org.slf4j
slf4j-log4j12
1.7.2
新建src/main/config资源包
在Eclipse上点击当前项目名,右键new-sourcefolder,输出src/main/config,点击确定,然后在src/main/config下新建一个log4properties文件,内容如下。
### 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
新建这个文件夹是上面log4properties文件我们设置的日志保存文件路径是在./log文件夹下。大致的项目结构如下
主要是把在测试断言中经常需要拿到的响应状态码和json解析之前需要把响应内容转换成json格式,这部分内容给提取到方法里。然后我们写上log输出。
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 headermap) throws ClientProtocolException, IOException {
//创建一个可关闭的HttpClient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
//创建一个HttpGet的请求对象
HttpGet httpget = new HttpGet(url);
//加载请求头到httpget对象
for(Map.Entry 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 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 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 headerMap) throws ClientProtocolException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPut httpput = new HttpPut(url);
httpput.setEntity(new StringEntity(entityString));
for(Map.Entry entry : headerMap.entrySet()) {
httpput.addHeader(entry.getKey(), entry.getValue());
}
//发送put请求
CloseableHttpResponse httpResponse = httpclient.execute(httpput);
return httpResponse;
}
/**
* 封装 delete请求方法,参数和get方法一样
* @param url, 新浪股票交易接口api,接口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;
}
}
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('当前测试新浪股票交易接口api,接口的完整地址为:'+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('新浪股票交易接口api,接口内容响应断言');
Assert.assertEquals(s, 'Eve','first name is not Eve');
Log.info('用例执行结束...');
}
}
这里,我强调下,我在上面BeforeClass部分无法引入Log输出,上面代码我注销了日志打印,如果不注销,这块会报空指针异常,很奇怪,只有在@BeforeClass中报错,在@Test中没有,我花了一些时间,还是搞不懂这块,就放弃在beforeclass部分添加日志输出。
看看在./log/aplog的日志输出效果
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 - 新浪股票交易接口api,接口内容响应断言
2018-05-26 17:39:23 300 INFO GetApiTest:57 - 用例执行结束...
说明:
这个Java新浪股票交易接口api,接口自动化测试框架,更适合于新浪股票交易接口api,接口的单元测试。也就是说,写新浪股票交易接口api,接口测试用例的人员必须会Java,必须掌握TestNG的基本使用。而且所有的新浪股票交易接口api,接口测试用例都是以一个个不同TestNG类文件组成。这个框架无法帮助黑盒测试人员完成新浪股票交易接口api,接口自动化测试,只适合会写单元测试的自动化测试人员,依赖单元测试框架去管理和运行新浪股票交易接口api,接口测试用例,拿到测试报告。后续可以扩展支持Jenkins持续集成和测试。
由于个人在大型项目新浪股票交易接口api,接口自动化方面经验欠缺,只能完成目前这个简单的新浪股票交易接口api,接口自动化测试框架。这个新浪股票交易接口api,接口自动化测试框架设计系列文章就先到这里结束。贴出整个项目的完整源码,百度云链接,点击这里。
018-08-21更新
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点