文章目录
本文任务
搭建一个以TestNG为测试框架,以Maven构建项目测试的demo项目,测试内容主要由Excel文件导入服务接口地址和请求体数据,最后将测试结果写入Excel文件。
依赖
主要依赖一下包:
- testng 测试框架
- fastjson 做字符串和json结构之间的转换
- gson 接收@RequestBody String xx, 提取xx
- jsonassert 比较两个json是否相同
- poi Java API To Access Microsoft Format Files
- maven-surefire-plugin 这个apache官网也说了
就是用来做单元测试的插件。
代码结构
重点关注标黄的代码及文件即可
相关代码文件
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.lzy.learning</groupId>
<artifactId>proj-auto-api-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>proj-auto-api-test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
<testResource>
<directory>${project.basedir}/src/main/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<configuration>
<forkCount>0</forkCount>
<testFailureIgnore>true</testFailureIgnore>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
MyExcelReader.java
package org.lzy.learning.proj_auto_api_test.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class MyExcelReader {
private static final String EXCEL_XLS = "xls";
private static final String EXCEL_XLSX = "xlsx";
private static Workbook getWorkbok(File file) throws IOException{
Workbook wb = null;
FileInputStream in = new FileInputStream(file);
if(file.getName().endsWith(EXCEL_XLS)){ //Excel 2003
wb = new HSSFWorkbook(in);
}else if(file.getName().endsWith(EXCEL_XLSX)){ // Excel 2007/2010
wb = new XSSFWorkbook(in);
}
return wb;
}
public static void writeExcel(List<List<String>> dataList, String finalXlsxPath, String sheetName){
OutputStream out = null;
try {
File file=new File(finalXlsxPath);
// if(!file.exists())
// {
// try {
// file.createNewFile();
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// 读取Excel文档
File finalXlsxFile = file;
Workbook workBook = getWorkbok(finalXlsxFile);
// sheet 对应一个工作页
// Sheet newSheet = workBook.createSheet();
Sheet sheet = workBook.getSheet(sheetName);
if (sheet == null) {
sheet = workBook.createSheet(sheetName);
}
CellStyle style_FAILED = workBook.createCellStyle();
style_FAILED.setFillForegroundColor(IndexedColors.RED.getIndex());
style_FAILED.setFillPattern(CellStyle.SOLID_FOREGROUND);
CellStyle style_PASSED = workBook.createCellStyle();
style_PASSED.setFillForegroundColor(IndexedColors.GREEN.getIndex());
style_PASSED.setFillPattern(CellStyle.SOLID_FOREGROUND);
//删除原有数据,除了属性列
int rowNumber = sheet.getLastRowNum();
for (int i = 1; i <= rowNumber; i++) {
Row row = sheet.getRow(i);
sheet.removeRow(row);
}
// 创建文件输出流,输出电子表格:这个必须有,否则你在sheet上做的任何操作都不会有效
out = new FileOutputStream(finalXlsxPath);
workBook.write(out);
//写一行
for (int j = 0; j < dataList.size(); j++) {
Row row = sheet.createRow(j);
//写一列
List<String> datas = dataList.get(j);
for (int k=0; k<datas.size(); k++) {
Cell cell = row.createCell(k);
cell.setCellValue(datas.get(k));
if ( "FAILED".equals(datas.get(k)))
cell.setCellStyle(style_FAILED);
if ("PASSED".equals(datas.get(k)))
cell.setCellStyle(style_PASSED);
}
}
// 创建文件输出流,准备输出电子表格:这个必须有,否则你在sheet上做的任何操作都不会有效
out = new FileOutputStream(finalXlsxPath);
workBook.write(out);
System.out.println("data exportation done!");
} catch (Exception e) {
System.out.println("manually creating a raw XLSX file is required!");
e.printStackTrace();
} finally{
try {
if(out != null){
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static Map<String, List<List<String>>> excelFile2Map(String fileName){
File excelFile = new File(fileName);
XSSFWorkbook wb = null;
try {
wb = new XSSFWorkbook(new FileInputStream(excelFile));
} catch (IOException e) {
e.printStackTrace();
}
int numberOfSheets = wb.getNumberOfSheets();
Map<String, List<List<String>>> ml = new HashMap<String, List<List<String>>>();
for (int x = 0; x < numberOfSheets; x++) {
XSSFSheet sheet = wb.getSheetAt(x);
String thisSheetName = sheet.getSheetName();
int columnNum = 0;
if (sheet.getRow(0) != null) {
columnNum = sheet.getRow(0).getLastCellNum()
- sheet.getRow(0).getFirstCellNum();
}
if (columnNum > 0) {
List<List<String>> lr = new ArrayList<List<String>>();
for (Row row : sheet) {
List<String> singleRow = new ArrayList<String>();
int n = 0;
for (int i = 0; i < columnNum; i++) {
Cell cell = row.getCell(i, Row.CREATE_NULL_AS_BLANK);
switch (cell.getCellType()) {
case Cell.CELL_TYPE_BLANK:
singleRow.add(null);
break;
case Cell.CELL_TYPE_BOOLEAN:
singleRow.add(Boolean.toString(cell.getBooleanCellValue()));
break;
// 数值
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
SimpleDateFormat sdf = null;
if (cell.getCellStyle().getDataFormat() == HSSFDataFormat
.getBuiltinFormat("h:mm")) {
sdf = new SimpleDateFormat("HH:mm");
} else {// 日期
sdf = new SimpleDateFormat("yyyy-MM-dd");
}
Date date = cell.getDateCellValue();
} else {
cell.setCellType(Cell.CELL_TYPE_STRING);
String temp = cell.getStringCellValue();
// 判断是否包含小数点,如果不含小数点,则以字符串读取,如果含小数点,则转换为Double类型的字符串
if (temp.indexOf(".") > -1) {
singleRow.add(String.valueOf(new Double(temp)).trim());
} else {
singleRow.add(temp.trim());
}
}
break;
case Cell.CELL_TYPE_STRING:
singleRow.add(cell.getStringCellValue().trim());
break;
case Cell.CELL_TYPE_ERROR:
singleRow.add("");
break;
case Cell.CELL_TYPE_FORMULA:
cell.setCellType(Cell.CELL_TYPE_STRING);
String temp = cell.getStringCellValue();
// 判断是否包含小数点,如果不含小数点,则以字符串读取,如果含小数点,则转换为Double类型的字符串
if (temp.indexOf(".") > -1) {
temp = String.valueOf(new Double(temp))
.trim();
Double cny = Double.parseDouble(temp);//6.2041
DecimalFormat df = new DecimalFormat("0.00");
String CNY = df.format(cny);
} else {
singleRow.add(temp.trim());
}
default:
singleRow.add("");
break;
}
}
lr.add(singleRow);
}
ml.put(thisSheetName, lr);
}
}
return ml;
}
}
WebRequest.java
package org.lzy.learning.proj_auto_api_test.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
public class WebRequest {
public static String sendRequest(String url, Object object) throws Exception {
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
conn.setConnectTimeout(1000000);
// 设置通用的请求属性
conn.setRequestProperty("accept", "application/json");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent","Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Content-Type","application/json;charset=utf-8");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(object);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
} catch (Exception e) {
e.printStackTrace();
throw new Exception("发送 POST 请求出现异常!" + e);
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result.toString();
}
}
App.java
package org.lzy.learning.proj_auto_api_test;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
public String sayHello() {
return "hellolzy";
}
}
MainTest.java
package org.lzy.learning.proj_auto_api_test;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MainTest {
private App app;
@BeforeMethod
public void init() {
app = new App();
}
@Test
public void testSayHello() {
Assert.assertEquals(app.sayHello(), "hellolzy");
}
}
Run1Test.java
package org.lzy.learning.proj_auto_api_test;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.lzy.learning.proj_auto_api_test.util.MyExcelReader;
import org.lzy.learning.proj_auto_api_test.util.WebRequest;
import org.skyscreamer.jsonassert.JSONCompare;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.skyscreamer.jsonassert.JSONCompareResult;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.alibaba.fastjson.JSON;
public class Run1Test {
private List<List<String>> responsePOST = new ArrayList<List<String>>();
private List<List<String>> comparisonContent = new ArrayList<List<String>>();
private int passCounter = 0;
private int failedCounter = 0;
private String inFilePath = "";
private String outFilePath = "";
private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");//设置日期格式
private String startTime = "";
private String endTime = "";
@BeforeTest
@Parameters(value={"WORKBOOKDIR", "OUTPUTFILEDIR"})
public void checkFilesAvailable(String inputFilePath, String outputFilePath) throws FileNotFoundException {
File in = new File(inputFilePath);
if (!in.exists())
throw new FileNotFoundException("input file is not found.");
File out = new File(outputFilePath);
if (!out.exists())
throw new FileNotFoundException("output file is not found.");
inFilePath = inputFilePath;
outFilePath = outputFilePath;
startTime = df.format(new Date());// new Date()为获取当前系统时间
}
@DataProvider(name="workBookData")
public Iterator<Object[]> provideData() {
List<Object[]> testData = new ArrayList<Object[]>();
//fetch input data from XLSX file
Map<String, List<List<String>>> inputData = MyExcelReader.excelFile2Map(inFilePath);
List<List<String>> dataRows = inputData.get("input");
//remove table header
dataRows.remove(0);
for (List<String> rowRecord : dataRows) {
String ip_addr = rowRecord.get(2);
String port = rowRecord.get(3);
String service_route = rowRecord.get(4);
String sendObjStr = rowRecord.get(5);
Map<String, String> reqMap = JSON.parseObject(sendObjStr, Map.class);
String url = ip_addr + ":" + port + service_route;
testData.add(new Object[] {rowRecord.get(0), rowRecord.get(1), url, reqMap, rowRecord.get(6)});
}
return testData.iterator();
}
// @Test(dataProvider="workBookData")
public void apiTestNoCompare(String testId, String testCase, String url, Map<String, String> reqBody) throws Exception {
String resp = null;
try {
resp = WebRequest.sendRequest(url, reqBody);
}catch(Exception e) {
throw new Exception("apiTest METHOD failed: " + e);
}
// System.out.println(resp);
responsePOST.add(Arrays.asList(testId, testCase, resp));
}
@Test(dataProvider="workBookData")
public void apiTestWithCompare(String testId, String testCase, String url, Map<String, String> reqBody, String base_response) throws Exception {
String resp = null;
try {
resp = WebRequest.sendRequest(url, reqBody);
}catch(Exception e) {
throw new Exception("apiTest METHOD failed: " + e);
}
// System.out.println(resp);
responsePOST.add(Arrays.asList(testId, testCase, resp));
JSONCompareResult result = JSONCompare.compareJSON(base_response, resp, JSONCompareMode.NON_EXTENSIBLE);
if (result.passed()) {
comparisonContent.add(Arrays.asList(testId, testCase, "PASSED", resp));
comparisonContent.add(Arrays.asList("", "", "", base_response));
passCounter += 1;
}else {
comparisonContent.add(Arrays.asList(testId, testCase, "FAILED", resp));
comparisonContent.add(Arrays.asList("", "", "", base_response));
failedCounter += 1;
}
}
@AfterTest
public void doFinal() throws ParseException {
// for (List<String> l : testResult) {
// System.out.println(l.toString());
// }
float coverage = passCounter * 100.0f / (passCounter + failedCounter);
DecimalFormat decimalFormat = new DecimalFormat(".00");
//format 返回的是字符串
String percentage = decimalFormat.format(coverage);
comparisonContent.add(Arrays.asList("测试结果", percentage, "%", ""));
endTime = df.format(new Date());// new Date()为获取当前系统时间
String lapse = String.valueOf(df.parse(endTime).getTime() - df.parse(startTime).getTime());
responsePOST.add(Arrays.asList("测试开始时间", startTime));
responsePOST.add(Arrays.asList("测试结束时间", endTime));
responsePOST.add(Arrays.asList("测试耗时", lapse, "ms"));
comparisonContent.add(Arrays.asList("测试开始时间", startTime));
comparisonContent.add(Arrays.asList("测试结束时间", endTime));
comparisonContent.add(Arrays.asList("测试耗时", lapse, "ms"));
MyExcelReader.writeExcel(responsePOST, outFilePath, "response");
MyExcelReader.writeExcel(comparisonContent, outFilePath, "comparison");
}
}
TestMain2.java
package org.lzy.learning.proj_auto_api_test;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class TestMain2 {
private App app;
@BeforeMethod
public void init() {
app = new App();
}
@Test
public void testSayHello() {
Assert.assertEquals(app.sayHello(), "hellolzy");
}
}
testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="testALL" verbose="2" parallel="false">
<parameter name="WORKBOOKDIR" value="X:/data.xlsx"/>
<parameter name="OUTPUTFILEDIR" value="X:/result.xlsx"/>
<test name="test1">
<classes>
<class name="org.lzy.learning.proj_auto_api_test.Run1Test" />
<class name="org.lzy.learning.proj_auto_api_test.MainTest" />
<class name="org.lzy.learning.proj_auto_api_test.TestMain2" />
</classes>
</test>
</suite>
实践总结
- 特别注意下面的pom内容不要忘了加,然后testng.xml的路径不要搞错了。
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<configuration>
<forkCount>0</forkCount>
<testFailureIgnore>true</testFailureIgnore>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
- 这份代码,需要有X:/data.xlsx和X:/result.xlsx这两个文件,其中result.xlsx是手动新建的,里面不需要有内容,路径/名字对就行。data.xlsx文件内容如下
注意是在Input这个sheet名下噢。 - 最后还需要起一个后端把excel里的那些服务接口运行起来。
- 上述代码文件都安排好之后,用命令行在项目根目录下maven test一下,感受build success的快乐吧。
- 测试方法里面会对比实际请求的返回json数据和理论json进行比较。结果展示一下:
- 最后在cmd里边,cd到项目根路径,
mvn clean test -Dsurefire.suiteXmlFiles=src/test/resources/testng.xml -Dsurefire.forkCount=1 -Dsurefire.reuseFork=false
THANK YOU
有什么问题欢迎留言探讨哈,我会来看滴。谢谢大家