From fc4388a37049cc900ec51d38ab86f3490bbbfbe6 Mon Sep 17 00:00:00 2001 From: 20220044 <17777770044@qq.com> Date: Wed, 10 Jul 2024 11:32:48 +0800 Subject: [PATCH] ADD apitest via upload --- ApiTest.java | 302 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 ApiTest.java diff --git a/ApiTest.java b/ApiTest.java new file mode 100644 index 0000000..b73bea7 --- /dev/null +++ b/ApiTest.java @@ -0,0 +1,302 @@ +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"); + } + } + +} -- 2.34.1