package test.com.sen.api; import com.alibaba.fastjson.JSON; import com.sen.api.beans.ApiDataBean; import com.sen.api.configs.ApiConfig; import com.sen.api.excepions.ErrorRespStatusException; import com.sen.api.listeners.AutoTestListener; import com.sen.api.listeners.RetryListener; import com.sen.api.utils.*; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.*; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.message.BasicHeader; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.util.EntityUtils; import org.dom4j.DocumentException; import org.testng.Assert; import org.testng.ITestContext; import org.testng.annotations.*; import org.testng.annotations.Optional; import java.io.File; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.file.Paths; import java.util.*; import java.util.regex.Matcher; @Listeners({ AutoTestListener.class, RetryListener.class }) public class ApiTest extends TestBase { /** * api请求根路径 */ private static String rootUrl; /** * 根路径是否以‘/’结尾 */ private static boolean rooUrlEndWithSlash = false; /** * 所有公共header,会在发送请求的时候添加到http header上 */ private static Header[] publicHeaders; /** * 是否使用form-data传参 会在post与put方法封装请求参数用到 */ private static boolean requestByFormData = false; /** * 配置 */ private static ApiConfig apiConfig; /** * 所有api测试用例数据 */ protected List dataList = new ArrayList(); private static HttpClient client; /** * 初始化测试数据 * * @throws Exception */ @Parameters("envName") @BeforeSuite public void init(@Optional("api-config.xml") String envName) throws Exception { String configFilePath = Paths.get(System.getProperty("user.dir"), envName).toString(); ReportUtil.log("api config path:" + configFilePath); apiConfig = new ApiConfig(configFilePath); // 获取基础数据 rootUrl = apiConfig.getRootUrl(); rooUrlEndWithSlash = rootUrl.endsWith("/"); // 读取 param,并将值保存到公共数据map Map params = apiConfig.getParams(); setSaveDates(params); List
headers = new ArrayList
(); apiConfig.getHeaders().forEach((key, value) -> { Header header = new BasicHeader(key, value); if(!requestByFormData && key.equalsIgnoreCase("content-type") && value.toLowerCase().contains("form-data")){ requestByFormData=true; } headers.add(header); }); publicHeaders = headers.toArray(new Header[headers.size()]); client = new SSLClient(); client.getParams().setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, 60000); // 请求超时 client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 60000); // 读取超时 } @Parameters({ "excelPath", "sheetName" }) @BeforeTest public void readData(@Optional("case/api-data.xls") String excelPath, @Optional("Sheet1") String sheetName) throws DocumentException { dataList = readExcelData(ApiDataBean.class, excelPath.split(";"), sheetName.split(";")); } /** * 过滤数据,run标记为Y的执行。 * * @return * @throws DocumentException */ @DataProvider(name = "apiDatas") //注解中用name给这个dataprovider命名。 public Iterator getApiData(ITestContext context) throws DocumentException { List dataProvider = new ArrayList(); for (ApiDataBean data : dataList) { if (data.isRun()) { dataProvider.add(new Object[] { data }); } } return dataProvider.iterator(); } @Test(dataProvider = "apiDatas") //接收 名为apiDatas的dataProvider public void apiTest(ApiDataBean apiDataBean) throws Exception { ReportUtil.log("--- test start ---"); if (apiDataBean.getSleep() > 0) { // sleep休眠时间大于0的情况下进行暂停休眠 ReportUtil.log(String.format("sleep %s seconds", apiDataBean.getSleep())); Thread.sleep(apiDataBean.getSleep() * 1000); } String apiParam = buildRequestParam(apiDataBean); // 封装请求方法 HttpUriRequest method = parseHttpRequest(apiDataBean.getUrl(), apiDataBean.getMethod(), apiParam); String responseData; try { // 执行 HttpResponse response = client.execute(method); int responseStatus = response.getStatusLine().getStatusCode(); ReportUtil.log("返回状态码:"+responseStatus); if (apiDataBean.getStatus()!= 0) { Assert.assertEquals(responseStatus, apiDataBean.getStatus(), "返回状态码与预期不符合!"); } // else { // // 非2开头状态码为异常请求,抛异常后会进行重跑 // if (200 > responseStatus || responseStatus >= 300) { // ReportUtil.log("返回状态码非200开头:"+EntityUtils.toString(response.getEntity(), "UTF-8")); // throw new ErrorRespStatusException("返回状态码异常:" // + responseStatus); // } // } HttpEntity respEntity = response.getEntity(); Header respContentType = response.getFirstHeader("Content-Type"); if (respContentType != null && respContentType.getValue() != null && (respContentType.getValue().contains("download") || respContentType.getValue().contains("octet-stream"))) { String conDisposition = response.getFirstHeader( "Content-disposition").getValue(); String fileType = conDisposition.substring( conDisposition.lastIndexOf("."), conDisposition.length()); String filePath = "download/" + RandomUtil.getRandom(8, false) + fileType; InputStream is = response.getEntity().getContent(); Assert.assertTrue(FileUtil.writeFile(is, filePath), "下载文件失败。"); // 将下载文件的路径放到{"filePath":"xxxxx"}进行返回 responseData = "{\"filePath\":\"" + filePath + "\"}"; } else { // responseData = DecodeUtil.decodeUnicode(EntityUtils // .toString(respEntity)); responseData=EntityUtils.toString(respEntity, "UTF-8"); } } catch (Exception e) { throw e; } finally { method.abort(); } // 输出返回数据log ReportUtil.log("resp:" + responseData); // 验证预期信息 verifyResult(responseData, apiDataBean.getVerify(), apiDataBean.isContains()); // 对返回结果进行提取保存。 saveResult(responseData, apiDataBean.getSave()); } private String buildRequestParam(ApiDataBean apiDataBean) { // 分析处理预参数 (函数生成的参数) String preParam = buildParam(apiDataBean.getPreParam()); savePreParam(preParam);// 保存预存参数 用于后面接口参数中使用和接口返回验证中 // 处理参数 String apiParam = buildParam(apiDataBean.getParam()); return apiParam; } /** * 封装请求方法 * * @param url * 请求路径 * @param method * 请求方法 * @param param * 请求参数 * @return 请求方法 * @throws UnsupportedEncodingException */ private HttpUriRequest parseHttpRequest(String url, String method, String param) throws UnsupportedEncodingException { // 处理url url = parseUrl(url); ReportUtil.log("method:" + method); ReportUtil.log("url:" + url); ReportUtil.log("param:" + param.replace("\r\n", "").replace("\n", "")); //upload表示上传,也是使用post进行请求 if ("post".equalsIgnoreCase(method) || "upload".equalsIgnoreCase(method)) { // 封装post方法 HttpPost postMethod = new HttpPost(url); postMethod.setHeaders(publicHeaders); //如果请求头的content-type的值包含form-data 或者 请求方法为upload(上传)时采用MultipartEntity形式 HttpEntity entity = parseEntity(param,requestByFormData || "upload".equalsIgnoreCase(method)); postMethod.setEntity(entity); return postMethod; } else if ("put".equalsIgnoreCase(method)) { // 封装put方法 HttpPut putMethod = new HttpPut(url); putMethod.setHeaders(publicHeaders); HttpEntity entity = parseEntity(param,requestByFormData ); putMethod.setEntity(entity); return putMethod; } else if ("delete".equalsIgnoreCase(method)) { // 封装delete方法 HttpDelete deleteMethod = new HttpDelete(url); deleteMethod.setHeaders(publicHeaders); return deleteMethod; } else { // 封装get方法 HttpGet getMethod = new HttpGet(url); getMethod.setHeaders(publicHeaders); return getMethod; } } /** * 格式化url,替换路径参数等。 * * @param shortUrl * @return */ private String parseUrl(String shortUrl) { // 替换url中的参数 shortUrl = getCommonParam(shortUrl); if (shortUrl.startsWith("http")) { return shortUrl; } if (rooUrlEndWithSlash == shortUrl.startsWith("/")) { if (rooUrlEndWithSlash) { shortUrl = shortUrl.replaceFirst("/", ""); } else { shortUrl = "/" + shortUrl; } } return rootUrl + shortUrl; } /** * 格式化参数,如果是from-data格式则将参数封装到MultipartEntity否则封装到StringEntity * @param param 参数 * @param formData 是否使用form-data格式 * @return * @throws UnsupportedEncodingException */ private HttpEntity parseEntity(String param,boolean formData) throws UnsupportedEncodingException{ if(formData){ Map paramMap = JSON.parseObject(param, HashMap.class); MultipartEntity multiEntity = new MultipartEntity(); for (String key : paramMap.keySet()) { String value = paramMap.get(key); Matcher m = funPattern.matcher(value); if (m.matches() && m.group(1).equals("bodyfile")) { value = m.group(2); multiEntity.addPart(key, new FileBody(new File(value))); } else { multiEntity.addPart(key, new StringBody(paramMap.get(key))); } } return multiEntity; }else{ return new StringEntity(param, "UTF-8"); } } }