<|start_filename|>src/main/java/com/utils/StringUtils.java<|end_filename|>
package com.utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtils {

    /**
     * 使用正则表达式提取字符串内容
     *
     * @param content 提取的字符串
     * @param regex 正则表达式
     *
     * @return 提取内容
     */
    public static String getMatchString(String content, String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            return matcher.group();
        } else {
            return null;
        }
    }

    /**
     * 判断当前正则表达式是否匹配整个字符串
     *
     * @param content 匹配的字符串
     * @param regex 正则表达式
     *
     * @return 是否匹配成功
     */
    public static boolean isMatchAll(String content, String regex) {
        String matchResult = StringUtils.getMatchString(content, regex);
        if (matchResult != null && matchResult.equals(content)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断当前正则表达式是否匹配部分字符串
     *
     * @param content 匹配的字符串
     * @param regex 正则表达式
     *
     * @return 是否匹配成功
     */
    public static boolean isMatchPart(String content, String regex) {
        String matchResult = StringUtils.getMatchString(content, regex);
        if (matchResult != null) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 分隔字符串
     *
     * @param split 分隔的字符串
     * @param regex 分隔符（支持正则表达式）
     *
     * @return 分隔后的字符串数组
     */
    public static String[] getSplitString(String split, String regex) {
        return split.split(regex);
    }

    /**
     * 判断输入的字符串参数是否为空。
     *
     * @param content 输入的字符串
     *
     * @return 输入的字符串为空则返回true,反之则返回false
     */
    public static boolean isEmpty(String content) {
        if (content == null || content.length() == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断输入的字符串参数是否不为空。
     *
     * @param content 输入的字符串
     *
     * @return 输入的字符串不为空则返回true,反之则返回false
     */
    public static boolean isNotEmpty(String content) {
        if(content != null && content.length() != 0) {
            return true;
        } else {
            return false;
        }
    }
}

<|start_filename|>src/main/java/com/utils/ExcelUtils.java<|end_filename|>
package com.utils;

import com.interfaceData.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.util.*;

/**
 * 读写 Excel 中不同 sheet 的表数据
 * 
 * @version 2.0
 * 
 */

public class ExcelUtils {
	
	private static Logger logger = LogManager.getLogger();
	
	public final static String XLSX = "xlsx";
	public final static String CSV = "csv";
	public final static String PUBLIC_PARAMETERS_SHEET = "公共参数";

	private String filePath = "";
	private static String fileFullPath;
	private String fileName;
	
	private Workbook workbook = null;
	
	/**
	 * 读取 excel 的构造函数，传进完成的文件路径，包含文件名称
	 * 
	 * @param fileFullPath 包含文件名称的完整路径
	 * 
	 */
	public ExcelUtils(String fileFullPath) {
		ExcelUtils.fileFullPath = fileFullPath;
		logger.info("Excel 文件绝对路径：" + fileFullPath);
		String regex = File.separator;
		//不同系统，分割的字符不一样
		if (System.getProperty("os.name").toLowerCase().contains("windows")) {
			regex = "\\\\";
		}
		String[] breakupString = StringUtils.getSplitString(fileFullPath, regex);
		fileName = breakupString[breakupString.length - 1];
		logger.info("Excel 文件名称：" + fileName);
		for (int i = 0; i < breakupString.length - 1; i ++) {
			filePath = filePath + breakupString[i] + File.separator;
		}
		logger.info("Excel 所在的文件路径：" + filePath);
		this.getExcelFile(fileFullPath);
	}

	/**
	 * 打开 Excel 文件，并返回一个 FileInputStream 对象
	 *
	 * @param fileFullPath Excel文件的绝对路径
	 *
	 * @return FileInputStream
	 */
	public FileInputStream getExcelFile(String fileFullPath) {
		FileInputStream fis = new FileUtils().setFileInputStream(fileFullPath);
		try {
			if (this.getExcelType().equalsIgnoreCase(XLSX)) {
				workbook = new XSSFWorkbook(fis); 						//读取excel文件
			}
		} catch (IOException e) {
			logger.error("打开文件时\"" + fileFullPath + "\"出现错误。\n", e);
		}
		return fis;
	}

	/**
	 * 将输入流关闭
	 *
	 * @param fis 输入流
	 */
	public void closeExcelFile(InputStream fis) {
		try {
			fis.close();
		} catch (IOException e) {
			logger.error("关闭文件出现错误。\n", e);
		}
	}

	/**
	 * 获取 Excel 文件的类型，如果不是 Excel 文件，返回空指针
	 * 
	 * @return Excel 文件类型，如：xls、xlsx等
	 */
	public String getExcelType() {
		if (fileName.toLowerCase().endsWith(XLSX)) {
			return XLSX;
		} else if (fileName.toLowerCase().endsWith(CSV)) {
			return CSV;
		} else {
			logger.error("所读文件不是为 Excel 文件", new FileNotFoundException("This file isn't excel."));
			return null;
		}
	}

	/**
	 * 获得 Excel 文件的所有 sheet 名字
	 * 
	 * @return Excel 的 sheetName 数组
	 */
	public List<String> getSheetNames() {
		//用一个字符串数组，存储 excel 的 sheet 名字
		int sheetNumbers = workbook.getNumberOfSheets();
		List<String> sheetNames = new ArrayList<>(sheetNumbers);
		String sheetNameLog = "[" + fileName + "] 文件有 " + sheetNumbers + " 个表，表名：\"";
		for (int i = 0; i < sheetNumbers; i++) {
			sheetNames.add(workbook.getSheetName(i));
			//log描述
			sheetNameLog = sheetNameLog + sheetNames.get(i) + "\"";
			if (i < sheetNumbers - 1) {
				sheetNameLog = sheetNameLog + ", \"";
			} else {
				sheetNameLog = sheetNameLog + " ";
			}
		}
		logger.info(sheetNameLog);
		return sheetNames;
	}
	
	/**
	 * 判断 sheet 是否存在
	 *
	 * @param sheetName 表名
	 * @return 指定的 sheet 是否存在
	 */
	public boolean isSheetExist(String sheetName) {
		List<String> sheetNames = getSheetNames();
		boolean isExist = false;
		for (String name : sheetNames) {
			if (sheetName.equalsIgnoreCase(name)) {
				isExist = true;
			}
		}
		//日志输出
		if (isExist) {
			logger.info("表：\"" + sheetName + "\" 存在");
		} else {
			logger.info("表：\"" + sheetName + "\" 不存在");
		}
		return isExist;
	}

	/**
	 * 获得一个接口测试的全部数据，
	 * 包括：路径，描述，参数组合
	 *
	 * @param sheetName 表名（接口名称）
	 * @return 接口测试的全部数据
	 */
	public InterfaceData getInterfaceData(String sheetName) {
		//如果找不到指定的接口，返回接口数据为空
		if (!this.isSheetExist(sheetName)) {
			logger.error("无法查找到接口 [" + sheetName + "] 的测试数据。");
			return null;
		}

		//接口测试用例模板，接口路径放在第一行的第二列
		String path = this.getCellData(sheetName, 0, 1);
		//接口测试用例模板，接口描述在第二行的第二列
		String desc = this.getCellData(sheetName,1, 1);

		final int paramKeyRowNum = 3;
		Sheet sheet = workbook.getSheet(sheetName);
		//接口测试用例模板，接口参数放在第四行
		int rowNums = sheet.getLastRowNum();
		int cellNums = sheet.getRow(paramKeyRowNum).getPhysicalNumberOfCells();
		Row row;
		Cell cell;

		//获取参数表头的值
		String[] paramKeys = this.getRowData(sheetName, paramKeyRowNum, cellNums);

		//获取参数名
		List<String> paramNames = new ArrayList<>();
		//默认不读取第一个，接口的调用方法
		for (int i = 1; i < paramKeys.length; i++) {
			String param = paramKeys[i];
			//忽略除参数名以外的其他字段
			if (param.equals(InterfaceParameters.EXPECT) ||
				param.equals(InterfaceParameters.ACTUAL) ||
				param.equals(InterfaceParameters.IS_PASS) ||
				param.equals(InterfaceParameters.FULL_URL) ||
				param.equals(InterfaceParameters.EXTRACTED_PUBLIC_PARAMETERS)
					){
				continue;
			} else {
				paramNames.add(param);
			}
		}

		//初始化接口的基本信息
		InterfaceInfo interfaceInfo = new InterfaceInfo(sheetName, path, desc, paramNames);
		//初始化接口的参数组合信息
		List<InterfaceParameters> parametersList = new ArrayList<>(rowNums + 1);

		//两个循环，遍历所有的参数信息
		for (int i = paramKeyRowNum + 1; i <= rowNums; i++) {
			row = sheet.getRow(i);
			InterfaceParameters parameters = new InterfaceParameters();
			Map<String, String> paramMap = new HashMap<>();
			Map<String, ExcelCellData> writeDatasMap = new HashMap<>();

			for (int j = 0; j < paramKeys.length; j++) {
				cell = row.getCell(j);
				String content = null;
				//判断单元格的值是否为空
				if (cell != null) {
					cell.setCellType(CellType.STRING);
					content = cell.getStringCellValue();
				}

				String pKey = paramKeys[j];
				//第一列为接口调用方法
				if (j == 0) {
					parameters.setMethod(content);
					//接口完整 url
				} else if (pKey.equals(InterfaceParameters.FULL_URL)) {
					writeDatasMap.put(InterfaceParameters.FULL_URL, new ExcelCellData(i, j, content));
					//预期结果读取
				} else if (pKey.equals(InterfaceParameters.EXPECT)) {
					parameters.setExpectResult(content);
					//实际测试结果读取
				} else if (pKey.equals(InterfaceParameters.ACTUAL)) {
					writeDatasMap.put(InterfaceParameters.ACTUAL, new ExcelCellData(i, j, content));
					//测试是否通过结果读取
				} else if (pKey.equals(InterfaceParameters.IS_PASS)) {
					writeDatasMap.put(InterfaceParameters.IS_PASS, new ExcelCellData(i, j, content));
					//提取公共参数
				} else if (pKey.equals(InterfaceParameters.EXTRACTED_PUBLIC_PARAMETERS)) {
					parameters.setExtractedPublicParameters(content);
					//接口参数读取
				} else {
					//读取公共参数，参数名支持大写字母（A-Z）、小写字母（a-z）、数字（0-9）、下划线（_）、破折号（-）
					if (content.startsWith("${") && content.endsWith("}")) {
						String matchString = StringUtils.getMatchString(content, "[a-zA-Z-0-9_\\--\\-]+");
						String publicParameterValue = this.getPublicParameter(matchString);
						if (publicParameterValue != null) {
							content = publicParameterValue;
						}
					}
					paramMap.put(paramKeys[j], content);
				}
			}
			//设置一次参数组合中，需要写入的数据集合
			parameters.setExcelWriteDatas(new ExcelWriteDatas(writeDatasMap));
			//设置接口参数
			parameters.setParameters(paramMap);
			parametersList.add(parameters);
			logger.info("已读取接口参数：" + parameters.toString());
		}
		InterfaceData interfaceData = new InterfaceData(interfaceInfo, parametersList);
		logger.info("获取到接口的具体信息：" + interfaceData);
		return interfaceData;
	}

	/**
	 * 获取接口测试的配置数据，
	 * 包括：http 协议类型，
	 * 		接口测试主 url，
	 * 		测试接口测试数据结果
	 *
	 * @return 接口配置数据
	 */
	public ConfigData getInterfaceConfig() {
		String sheetName = ConfigData.CONFIG_SHEET_NAME;
		//获取所测试接口的协议，在模板中放在第一行第二列
		String scheme = this.getCellData(sheetName, 0, 1);
		//获取接口测试的主 URL，放在第二行的第二列
		String host = this.getCellData(sheetName,1,1);
		//将接口的 http 协议和主 url 封装成一个类
		InterfaceMainURL mainURL = new InterfaceMainURL(scheme, host);

		final int suiteKeyRowNum = 3;
		Sheet sheet = workbook.getSheet(sheetName);
		//接口测试用例模板，接口测试结果表头放在第四行
		int rowNums = sheet.getLastRowNum();
		int cellNums = sheet.getRow(suiteKeyRowNum).getPhysicalNumberOfCells();
		Row row;
		Cell cell;

		List<SuiteConfig> suiteConfigList = new ArrayList<>(rowNums + 1);

		//获取参数表头的值
		String[] paramKeys = this.getRowData(sheetName, suiteKeyRowNum, cellNums);
		//两个循环，遍历所有的参数信息
		for (int i = suiteKeyRowNum + 1; i <= rowNums; i++) {
			row = sheet.getRow(i);
			SuiteConfig suiteConfig = new SuiteConfig();

			for (int j = 0; j < paramKeys.length; j++) {
				cell = row.getCell(j);
				String content = null;
				//判断单元格的值是否为空
				if (cell != null) {
					cell.setCellType(CellType.STRING);
					content = cell.getStringCellValue();
				}
				switch (j) {
					//测试集合 id
					case 0:
						suiteConfig.setId(content);
						break;
					//测试接口名称
					case 1:
						suiteConfig.setInterfaceName(content);
						break;
					//运行模式
					case 2:
						suiteConfig.setRunMode(content);
						break;
					//测试结果
					case 3:
						suiteConfig.setExcelCellData(new ExcelCellData(i, j, content));
						break;
					default:
						break;
				}
			}
			suiteConfigList.add(suiteConfig);
			logger.info(suiteConfig.toString());
		}
		ConfigData configData = new ConfigData(mainURL, suiteConfigList);
		logger.info(configData);
		return configData;
	}

	/**
	 * 设置公共参数
	 *
	 * @param parameters 参数集合
	 *
	 */
	public void setPublicParameters(Map<String, String> parameters) {
		final int Key_Cell_Num = 0;
		final int Value_Cell_Num = 1;

		//不存在公共参数 sheet，则添加
		if (!this.isSheetExist(PUBLIC_PARAMETERS_SHEET)) {
			logger.info("添加表： [" + PUBLIC_PARAMETERS_SHEET + "]");
			workbook.createSheet(PUBLIC_PARAMETERS_SHEET);
		}
		Set<String> parameterKeys = parameters.keySet();
		Sheet publicParametersSheet = workbook.getSheet(PUBLIC_PARAMETERS_SHEET);
		Row row;
		Cell valueCell;
		int lastRowNum = publicParametersSheet.getLastRowNum();
		boolean isContent = false;
		//判断第一行是否有数据
		if (publicParametersSheet.getRow(0) != null) {
			isContent = true;
			//对公共参数 sheet 的值，从第一条开始遍历
			for (int i = 0; i <= lastRowNum; i++) {
				row = publicParametersSheet.getRow(i);
				if (row == null) {
					continue;
				}
				valueCell = row.getCell(Key_Cell_Num);
				String existKey = null;
				if (valueCell != null) {
					valueCell.setCellType(CellType.STRING);
					existKey = valueCell.getStringCellValue();
				}
				//想要加入的公共参数已经存在，则覆盖（此逻辑以后可更改）
				if (parameterKeys.contains(existKey)) {
					valueCell = row.getCell(Value_Cell_Num, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
					if (valueCell == null) {
						valueCell = row.createCell(Value_Cell_Num);
					}
					String existKeyParameter = parameters.get(existKey);
					valueCell.setCellValue(existKeyParameter);		//向指定的单元格输入内容
					logger.info("覆盖已存在的公共参数 [" + existKey + "]，重新写入值：[" + existKeyParameter + "]");
					//避免二次删除
					parameterKeys.remove(existKey);
				}
			}
		}

		//isContent 为 true，则表明公共参数表原来有数据，需要加1
		if (isContent) {
			lastRowNum ++;
		}
		//将剩下与原有参数不匹配的参数和值写入到公共参数 sheet 里
		Cell keyCell;
		for (String key : parameterKeys) {
			//写入参数的 key 和 value
			row = publicParametersSheet.getRow(lastRowNum);
			if (row == null) {
				row = publicParametersSheet.createRow(lastRowNum);
			}
			//写入参数名称
			keyCell = row.getCell(Key_Cell_Num, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
			if (keyCell == null) {
				keyCell = row.createCell(Key_Cell_Num);
			}
			keyCell.setCellType(CellType.STRING);
			//写入参数值
			keyCell.setCellValue(key);valueCell = row.getCell(Value_Cell_Num, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
			if (valueCell == null) {
				valueCell = row.createCell(Value_Cell_Num);
			}
			String keyParameter = parameters.get(key);
			valueCell.setCellValue(keyParameter);

			lastRowNum ++;
			logger.info("写入新的公共参数：[" + key + "], 值：[" + keyParameter + "]");
		}
		this.writeWorkbook();
	}

	/**
	 * 获取公共参数
	 *
	 * @param paramName 参数名
	 * @return 公共参数字段
	 */
	public String getPublicParameter(String paramName) {
		String content = null;
		Sheet publicSheet = workbook.getSheet(PUBLIC_PARAMETERS_SHEET);
		int lastRowNum = publicSheet.getLastRowNum();
		Row pRow;
		Cell pCell;
		//查找符合的公共参数，并读取它的值
		for (int pn = 0; pn <= lastRowNum; pn++) {
			pRow = publicSheet.getRow(pn);
			if (pRow != null) {
				pCell = pRow.getCell(0);
				if (pCell != null) {
					pCell.setCellType(CellType.STRING);
					String cellValue = pCell.getStringCellValue();
					if (paramName.equals(cellValue)) {
						pCell = pRow.getCell(1);
						pCell.setCellType(CellType.STRING);
						content = pCell.getStringCellValue();
					}
				}
			}
		}
		return content;
	}

	/**
	 * 将接口测试的结果写入到测试用例文档中
	 *
	 * @param interfaceData 接口测试数据
	 */
	public void writeInterfaceExpectResult(InterfaceData interfaceData) {
		//接口名称
		String interfaceName = interfaceData.getInterfaceInfo().getName();
		//提取接口参数中，写入的测试结果
		List<InterfaceParameters> interfaceParametersList = interfaceData.getParametersList();
		List<ExcelWriteDatas> excelWriteDatasList = new ArrayList<>(interfaceParametersList.size());
		for (int i = 0; i < interfaceParametersList.size(); i++) {
			excelWriteDatasList.add(interfaceParametersList.get(i).getExcelWriteDatas());
		}
		logger.info("正在往 [" + interfaceName + "] 接口写入测试结果");

		this.writeSheetResult(interfaceName, excelWriteDatasList);
	}

	/**
	 * 将测试集合结果写入到文件中
	 *
	 * @param configData 配置数据
	 */
	public void writeSuiteResult(ConfigData configData) {
		//获取测试集合测试结果
		List<SuiteConfig> suiteConfigList = configData.getSuiteConfigList();
		List<ExcelWriteDatas> writeDatasList = new ArrayList<>(suiteConfigList.size());

		for (int i = 0; i < suiteConfigList.size(); i++) {
			ExcelCellData cellData = suiteConfigList.get(i).getExcelCellData();
			Map<String, ExcelCellData> cellDataMap = new HashMap<>();
			cellDataMap.put(SuiteConfig.ACTUAL, cellData);
			writeDatasList.add(new ExcelWriteDatas(cellDataMap));
		}

		logger.info("正在往 [" + ConfigData.CONFIG_SHEET_NAME + "] 表写入测试集合总体结果");
		this.writeSheetResult(ConfigData.CONFIG_SHEET_NAME, writeDatasList);
	}

	/**
	 * 将指定 sheet 的测试结果写入到文件中
	 *
	 * @param sheetName 表名
	 * @param excelWriteDatasList 需写入的测试结果集合
	 */
	private void writeSheetResult(String sheetName, List<ExcelWriteDatas> excelWriteDatasList) {
		Sheet sheet = workbook.getSheet(sheetName);
		Row row;
		Cell cell;

		for (int i = 0; i < excelWriteDatasList.size(); i++) {
			ExcelWriteDatas excelWriteDatas = excelWriteDatasList.get(i);
			//获取写入数据的 key 值集合
			Map<String, ExcelCellData> excelCellDataMap = excelWriteDatas.getWriteDatasMap();
			Set<String> keysSet = excelCellDataMap.keySet();
			Iterator<String> iterator = keysSet.iterator();
			//遍历所有需要写入的参数
			while (iterator.hasNext()) {
				String paramKey = iterator.next();

				ExcelCellData excelCellData = excelCellDataMap.get(paramKey);
				int rowNum = excelCellData.getRowNum();
				int cellNum = excelCellData.getCellNum();
				String suiteResultString = excelCellData.getData();

				row = sheet.getRow(rowNum);			//获取到相应的行数
				cell = row.getCell(cellNum, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
				if (cell == null) {
					cell = row.createCell(cellNum);
				}
				cell.setCellValue(suiteResultString);		//向指定的单元格输入内容
				logger.info("在第 " + (rowNum + 1) + " 行，第 " + (cellNum + 1) + " 列，写入数据：\"" + suiteResultString + "\"");
			}
		}

		logger.info("对 [" + sheetName + "] 表，写入数据成功");
		//写入数据到 Excel 文件中
		this.writeWorkbook();
	}

	/**
	 * 获取一行的测试数据
	 * 
	 * @param sheetName 表名
	 * @param rowNum 行数
	 * @param cells 需要读取的列数目
	 * 
	 * @return 一行的测试用例数据
	 */
	public String[] getRowData(String sheetName, int rowNum, int cells) {
        Sheet sheet = workbook.getSheet(sheetName);
        Row row;
        Cell cell;

        logger.info("正在读取的表名：" + sheetName);
        
		//以 excel 文件一个表的行和列，定义一个二维数组
		String[] rowDatas = new String[cells];					//用来存储表数据的二维数组
		logger.info("正在读取第 " + rowNum + " 行");
		logger.info("需要读取的列数：" + cells);
		
		row = sheet.getRow(rowNum);
		for (int j = 0; j < cells; j++) {							//遍历每一列数据
			cell = row.getCell(j);
			if (cell != null) {										//如果单元格元素不为空，以字符串形式读取
				cell.setCellType(CellType.STRING);
				rowDatas[j] = cell.getStringCellValue();
			} else {												//如果单元格元素为空，则为空
				rowDatas[j] = null;
			}
		}
		return rowDatas;
	}

	/**
	 * 获取 excel 指定表名，指定单元格的数据，行、列数，从0开始
	 * 
	 * @param sheetName 表的名称
	 * @param rowNum 行数
	 * @param cellNum 列数
	 * 
	 * @return 单元格内容
	 */
	public String getCellData(String sheetName, int rowNum, int cellNum) {
		String cellData = null;
		try {
			Sheet sheet = workbook.getSheet(sheetName);
			Row row = sheet.getRow(rowNum);							//获取到相应的行数
			Cell cell = row.getCell(cellNum, Row.MissingCellPolicy.RETURN_NULL_AND_BLANK);						//获取到相应的列数
			if (cell != null) {										//如果单元格元素不为空，以字符串形式读取
				cell.setCellType(CellType.STRING);
				cellData = cell.getStringCellValue();
			} else {												//如果单元格元素为空，则为空
				cellData = null;
			}
			logger.info("在 sheet: [" + sheetName + "] 的第 " + (rowNum + 1) + " 行，第 " + (cellNum + 1) + " 列，读取数据：\"" + cellData + "\"");
		} catch (Exception e) {
			logger.error("在 sheet: [" + sheetName + "] 的第 " + (rowNum + 1) + " 行，第 " + (cellNum + 1) + " 列，读取数据失败。");
		}
		return cellData;
	}
	
	/**
	 * 写入一个数据到 excel 指定的单元格中（由行、列数标记），行、列数，从0开始
	 * 
	 * @param sheetName 表的名称
	 * @param rowNum 行数
	 * @param cellNum 列数
	 * @param testRsult 测试结果
	 * 
	 */
	private void writeCellData(String sheetName, int rowNum, int cellNum, String testRsult) {

        Sheet sheet = workbook.getSheet(sheetName);		//读取相应sheet
        
        Row row = sheet.getRow(rowNum);			//获取到相应的行数
        Cell cell = row.getCell(cellNum, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
        if (cell == null) {
            cell = row.createCell(cellNum);
        }
        cell.setCellValue(testRsult);		//向指定的单元格输入内容
        logger.info("在 sheet: [" + sheetName + "] 的第 " + (rowNum + 1) + " 行，第 " + (cellNum + 1) + " 列，写入数据：\"" + testRsult + "\"");

        //暂不写入到文件中，等待调用方法写入

	}

	/**
	 * 将缓存的 workbook 写入到实际的文档中
	 *
	 */
	private void writeWorkbook() {
		try {
			FileOutputStream out = new FileOutputStream(fileFullPath); //向指定的excel文件中写数据
			workbook.write(out);
			out.flush();
			out.close();
			logger.info("对 Excel文件写入数据成功。");
		} catch (IOException e) {
			logger.error("写入数据失败。\n", e);
		}
	}
}


<|start_filename|>src/test/java/com/engine/Excel_Engine_Test.java<|end_filename|>
package com.engine;

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.File;

public class Excel_Engine_Test {

    private Excel_Engine engine;

    @BeforeClass
    public void setUp() throws Exception {
        String excelPath = System.getProperty("user.dir") + File.separator + "test-datas" + File.separator + "interface-engine-test-demo.xlsx";
        engine = new Excel_Engine(excelPath);
    }

    @Test
    public void test_run() throws Exception {
        engine.runTest();
    }

}


<|start_filename|>src/main/java/com/interfaceData/InterfaceParameters.java<|end_filename|>
package com.interfaceData;

import org.apache.http.client.methods.*;

import java.util.Map;

/**
 * 接口调用方式、参数、预期结果、实际测试结果的数据类.
 * PS: 一个接口测试参数组合
 *
 * @version 1.0
 */

public class InterfaceParameters {

    public static final String FULL_URL = "完整URL";
    public static final String EXPECT = "预期结果";
    public static final String ACTUAL = "实际测试结果";
    public static final String IS_PASS = "是否通过测试";
    public static final String EXTRACTED_PUBLIC_PARAMETERS = "提取公共参数";

    //接口的调用方式，一般有 GET、POST 等
    private String method = "GET";
    //接口的参数数据
    private Map<String, String> parameters;
    //预期接口返回的字段或数据，可用于断言
    private String expectResult;

    public String extractedPublicParameters;
    //所有需要写入的接口相关数据
    private ExcelWriteDatas excelWriteDatas;

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        switch (method.toUpperCase()) {
            case HttpDelete.METHOD_NAME :
                this.method = HttpDelete.METHOD_NAME;
                break;
            case HttpGet.METHOD_NAME :
                this.method = HttpGet.METHOD_NAME;
                break;
            case HttpHead.METHOD_NAME :
                this.method = HttpHead.METHOD_NAME;
                break;
            case HttpOptions.METHOD_NAME :
                this.method = HttpOptions.METHOD_NAME;
                break;
            case HttpPatch.METHOD_NAME :
                this.method = HttpPatch.METHOD_NAME;
                break;
            case HttpPost.METHOD_NAME :
                this.method = HttpPost.METHOD_NAME;
                break;
            case HttpPut.METHOD_NAME :
                this.method = HttpPut.METHOD_NAME;
                break;
            case HttpTrace.METHOD_NAME :
                this.method = HttpTrace.METHOD_NAME;
                break;
            case "CONNECT" :
                this.method = "CONNECT";
                break;
            default:
                this.method = method;
                break;
        }
    }

    public Map<String, String> getParameters() {
        return parameters;
    }

    public void setParameters(Map<String, String> parameters) {
        this.parameters = parameters;
    }

    public String getExpectResult() {
        return expectResult;
    }

    public void setExpectResult(String expectResult) {
        this.expectResult = expectResult;
    }

    public String getExtractedPublicParameters() {
        return extractedPublicParameters;
    }

    public void setExtractedPublicParameters(String extractedPublicParameters) {
        this.extractedPublicParameters = extractedPublicParameters;
    }

    public ExcelWriteDatas getExcelWriteDatas() {
        return excelWriteDatas;
    }

    public void setExcelWriteDatas(ExcelWriteDatas excelWriteDatas) {
        this.excelWriteDatas = excelWriteDatas;
    }

    @Override
    public String toString() {
        return "InterfaceParameters{" +
                "method='" + method + '\'' +
                ", parameters=" + parameters +
                ", expectResult='" + expectResult + '\'' +
                "," + excelWriteDatas +
                '}';
    }
}


<|start_filename|>src/test/java/com/utils/ExcelUtilsTest.java<|end_filename|>
package com.utils;

import com.interfaceData.*;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExcelUtilsTest {

    private ExcelUtils excelUtils;
    private String testSheet;

    @BeforeClass
    public void beforeClass() {
        String excelPath = System.getProperty("user.dir") + File.separator + "test-datas" + File.separator + "interface-test-demo.xlsx";
        testSheet = "家长版登录";
        excelUtils = new ExcelUtils(excelPath);
    }

    @Test
    public void test_getExcelType() {
//        System.err.println(excelUtils.getExcelType());
        Assert.assertEquals(excelUtils.getExcelType(), ExcelUtils.XLSX);
    }

    @Test
    public void test_getSheetNames() {
        Assert.assertNotNull(excelUtils.getSheetNames());
    }

    @Test
    public void test_isSheetExist() {
        Assert.assertTrue(excelUtils.isSheetExist(testSheet));
    }

    @Test
    public void test_InterData() {
        InterfaceData data = excelUtils.getInterfaceData(testSheet);
//        System.err.println(data);
        Assert.assertNotNull(data);
    }

    @Test
    public void test_ConfigData() {
        ConfigData data = excelUtils.getInterfaceConfig();
//        System.err.println(data);
        Assert.assertNotNull(data);
    }

    @Test
    public void test_writeInterfaceResult() throws Exception {
        InterfaceData data = excelUtils.getInterfaceData(testSheet);
        List<InterfaceParameters> interfaceParametersList = data.getParametersList();

        for (int i = 0; i < interfaceParametersList.size(); i++) {
            interfaceParametersList.get(i).getExcelWriteDatas().getWriteDatasMap().get(InterfaceParameters.ACTUAL).setData("result-" + i);

            interfaceParametersList.get(i).getExcelWriteDatas().getWriteDatasMap().get(InterfaceParameters.IS_PASS).setData("isPass-" + i);
        }
        //data.setParametersList(interfaceParametersList);
        excelUtils.writeInterfaceExpectResult(data);
    }

    @Test
    public void test_writeSuiteResult() throws Exception {
        ConfigData configData = excelUtils.getInterfaceConfig();
        List<SuiteConfig> suiteConfigList = configData.getSuiteConfigList();
        for (int i = 0; i < suiteConfigList.size(); i++) {
            suiteConfigList.get(i).getExcelCellData().setData("result集合-" + i);
        }
        configData.setSuiteConfigList(suiteConfigList);
        excelUtils.writeSuiteResult(configData);
    }

    @Test
    public void test_setPublicParameters() throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("accesstoken", "<PASSWORD>");
        map.put("openid","139278492834");
        map.put("clientid", "2");
        map.put("mobile", "1298492235");
        excelUtils.setPublicParameters(map);
    }

}


<|start_filename|>src/main/java/com/http/MyURI.java<|end_filename|>
package com.http;

import java.net.URI;

/**
 * 为了更加适应业务的需要，封装这个类。
 * 此类包括：URL，请求Method
 *
 * @version 1.0
 */

public class MyURI {

    private final String method;
    private final URI uri;

    /**
     * 提供以下两个参数构建 MyURI 类
     *
     * @param method Http方法
     * @param uri 网址
     */
    public MyURI(String method, URI uri) {
        this.method = method;
        this.uri = uri;
    }

    public String getMethod() {
        return method;
    }

    public URI getUri() {
        return uri;
    }

    @Override
    public String toString() {
        return "MyURI{" +
                "method='" + method + '\'' +
                ", uri=" + uri +
                '}';
    }
}

<|start_filename|>src/main/java/com/interfaceData/ConfigData.java<|end_filename|>
package com.interfaceData;

import java.util.List;

/**
 * 接口测试配置表的数据类型，
 * 包括：http 协议类型，主 url，测试接口测试结果表
 *
 * @version 1.0
 */

public class ConfigData {

    public static final String CONFIG_SHEET_NAME = "配置";

    private InterfaceMainURL interfaceMainURL;
    private List<SuiteConfig> suiteConfigList;

    public ConfigData() {
    }

    /**
     * 接口参数配置
     *
     * @param interfaceMainURL 接口 url 的主干部分
     * @param suiteConfigList 接口参数的配置列表
     */
    public ConfigData(InterfaceMainURL interfaceMainURL, List<SuiteConfig> suiteConfigList) {
        this.interfaceMainURL = interfaceMainURL;
        this.suiteConfigList = suiteConfigList;
    }

    public InterfaceMainURL getInterfaceMainURL() {
        return interfaceMainURL;
    }

    public void setInterfaceMainURL(InterfaceMainURL interfaceMainURL) {
        this.interfaceMainURL = interfaceMainURL;
    }

    public List<SuiteConfig> getSuiteConfigList() {
        return suiteConfigList;
    }

    public void setSuiteConfigList(List<SuiteConfig> suiteConfigList) {
        this.suiteConfigList = suiteConfigList;
    }

    @Override
    public String toString() {
        return "ConfigData{" +
                "interfaceMainURL=" + interfaceMainURL +
                ", suiteConfigList=" + suiteConfigList +
                '}';
    }
}


<|start_filename|>src/main/java/com/interfaceData/ExcelWriteDatas.java<|end_filename|>
package com.interfaceData;

import java.util.Map;

public class ExcelWriteDatas {

    private Map<String, ExcelCellData> writeDatasMap;

    public ExcelWriteDatas(Map<String, ExcelCellData> writeDatasMap) {
        this.writeDatasMap = writeDatasMap;
    }

    public Map<String, ExcelCellData> getWriteDatasMap() {
        return writeDatasMap;
    }

    public void setWriteDatasMap(Map<String, ExcelCellData> writeDatasMap) {
        this.writeDatasMap = writeDatasMap;
    }

    @Override
    public String toString() {
        return "ExcelWriteDatas " + writeDatasMap;
    }
}


<|start_filename|>src/test/java/com/http/HttpDriverTest.java<|end_filename|>
package com.http;

import com.interfaceData.ConfigData;
import com.interfaceData.InterfaceData;
import com.interfaceData.InterfaceParameters;
import com.utils.ExcelUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.File;

public class HttpDriverTest {

    private ExcelUtils excelUtils;
    private String testSheet;

    private ConfigData configData;
    private InterfaceData interfaceData;
    private HttpDriver driver;

    private MyURI myURI;

    @BeforeClass
    public void setUp() throws Exception {
        String excelPath = System.getProperty("user.dir") + File.separator + "test-datas" + File.separator + "interface-test-demo.xlsx";
        testSheet = "家长版登录";
        excelUtils = new ExcelUtils(excelPath);

        configData = excelUtils.getInterfaceConfig();
        interfaceData = excelUtils.getInterfaceData(testSheet);

        driver = new HttpDriver(configData.getInterfaceMainURL());
    }

    @Test
    public void test_getURI() throws Exception {
        myURI = driver.getFullURI(interfaceData.getInterfaceInfo(), interfaceData.getParametersList().get(0));
        Assert.assertNotNull(myURI);
    }

    @Test
    public void test_Response() throws Exception {
        myURI = driver.getFullURI(interfaceData.getInterfaceInfo(), interfaceData.getParametersList().get(0));
        String responseData = driver.sendRequestAndGetResponse(myURI);
    }

    @Test
    public void test_failRequest() throws Exception {
        myURI = driver.getURI("GET", "htt");
        //返回值为空
        Assert.assertNull(driver.sendRequestAndGetResponse(myURI));
    }
}



<|start_filename|>src/main/java/com/utils/TestResult.java<|end_filename|>
package com.utils;

/**
 * 测试结果字段
 *
 * @version 1.0
 */

public enum TestResult {
    PASS,SKIP,FAIL
}

<|start_filename|>src/main/java/com/interfaceData/SuiteConfig.java<|end_filename|>
package com.interfaceData;

/**
 * 测试配置
 *
 * @version 1.0
 */

public class SuiteConfig {

    public static final String ACTUAL = "测试结果";

    //测试 id
    private String id;
    //接口描述
    private String interfaceName;
    //接口运行模式：是、否
    private String runMode;
    //测试结果
    private ExcelCellData actualesult;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public String getRunMode() {
        return runMode;
    }

    public void setRunMode(String runMode) {
        this.runMode = runMode;
    }

    public ExcelCellData getExcelCellData() {
        return actualesult;
    }

    public void setExcelCellData(ExcelCellData actualesult) {
        this.actualesult = actualesult;
    }

    @Override
    public String toString() {
        return "SuiteConfig{" +
                "id='" + id + '\'' +
                ", interfaceName='" + interfaceName + '\'' +
                ", runMode='" + runMode + '\'' +
                ", actualResult=" + actualesult +
                "}\n";
    }
}

<|start_filename|>src/test/java/com/utils/StringUtilsTest.java<|end_filename|>
package com.utils;

import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class StringUtilsTest {

    private String content;

    @BeforeClass
    public void setUp() throws Exception {
        content = "\"token\":\"<PASSWORD>\",\"openid\":2346462,\"name\":\"测试名字\"";
    }

    @Test
    public void test_getMatchString() throws Exception {
        String regex = "\"openid\":[0-9]+";
        String matchString = StringUtils.getMatchString(content, regex);
        System.out.println("matchString：" + matchString);
        Assert.assertNotNull(matchString);
    }

    @Test
    public void test_isMatchAll() throws Exception {
        String regex = "\"token\":\"[A-Za-z0-9_]+\",\"openid\":[0-9]+,\"name\":\"[\\S]+\"";
        boolean isMatchAll = StringUtils.isMatchAll(content, regex);
        System.out.println("isMatchAll：" + isMatchAll);
        Assert.assertTrue(isMatchAll);
    }

    @Test
    public void test_isMatchPart() throws Exception {
        String regex = "\"token\":\"[A-Za-z0-9_]+\"";
        boolean isMatchPart = StringUtils.isMatchPart(content, regex);
        System.out.println("isMatchPart：" + isMatchPart);
        Assert.assertTrue(isMatchPart);
    }

    @Test
    public void test_splitString() throws Exception {
        String[] splits = StringUtils.getSplitString(content,",");
        String log = "splitString：";
        for (String split : splits) {
            log = log + split + ",";
        }
        System.out.println(log);
        Assert.assertNotNull(splits);
    }

}


<|start_filename|>src/main/java/com/interfaceData/ExcelCellData.java<|end_filename|>
package com.interfaceData;

/**
 * 此类存储"实际测试结果"单元格的方位信息，
 * 以便测试完毕后，根据记录的位置信息，写入到指定的位置中
 *
 * @version 1.0
 */

public class ExcelCellData {

    private int rowNum;
    private int cellNum;
    private String data;

    /**
     * 声明类时，一次性设置类的参数
     *
     * @param rowNum 行号
     * @param cellNum 列号
     * @param data 实际测试结果内容
     */
    public ExcelCellData(int rowNum, int cellNum, String data) {
        this.rowNum = rowNum;
        this.cellNum = cellNum;
        this.data = data;
    }

    public int getRowNum() {
        return rowNum;
    }

    public void setRowNum(int rowNum) {
        this.rowNum = rowNum;
    }

    public int getCellNum() {
        return cellNum;
    }

    public void setCellNum(int cellNum) {
        this.cellNum = cellNum;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public String toString(){
        return data;
    }
}


<|start_filename|>src/main/java/com/interfaceData/InterfaceMainURL.java<|end_filename|>
package com.interfaceData;

/**
 * 接口的协议和域名部分
 *
 * @version 1.0
 */

public class InterfaceMainURL {

    private String scheme;
    private String host;

    /**
     * 声明就要提供 http 协议和域名
     *
     * @param scheme http 协议
     * @param host 域名
     */
    public InterfaceMainURL(String scheme, String host) {
        this.scheme = scheme;
        this.host = host;
    }

    public String getScheme() {
        return scheme;
    }

    public void setScheme(String scheme) {
        this.scheme = scheme;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    @Override
    public String toString() {
        return "InterfaceMainURL{\"" + scheme + "://" + host + "\"}";
    }
}


<|start_filename|>src/main/java/com/engine/Excel_Engine.java<|end_filename|>
package com.engine;

import com.http.MyURI;
import com.http.HttpDriver;
import com.interfaceData.*;
import com.utils.ExcelUtils;
import com.utils.StringUtils;
import com.utils.TestResult;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 接口测试引擎，负责遍历 Excel 接口测试用例，
 * 将需要测试的接口所填写的参数全部遍历一次。
 *
 * @version 1.0
 *
 * @since 2017.07.27
 *
 */

public class Excel_Engine {

    private Logger logger = LogManager.getLogger();

    private ExcelUtils excelUtils;
    private ConfigData configData;
    private String excelPath;
    //默认接口测试通过
    private boolean suiteIsPass = true;

    /**
     * 初始化引擎类，提供测试用例文档路径
     *
     * @param excelPath 测试用例文档路径
     *
     */
    public Excel_Engine(final String excelPath) {
        this.excelPath = excelPath;
        this.excelUtils = new ExcelUtils(excelPath);
        this.configData = excelUtils.getInterfaceConfig();
    }

    /**
     * 根据测试文档配置表数据运行测试
     *
     */
    public synchronized void runTest() {
        //接口集合
        List<SuiteConfig> suiteConfigList = configData.getSuiteConfigList();
        HttpDriver driver = new HttpDriver(configData.getInterfaceMainURL());

        for (int i = 0; i < suiteConfigList.size(); i++) {
            suiteIsPass = true;
            SuiteConfig suiteConfig = suiteConfigList.get(i);
            ExcelCellData suiteResult = suiteConfig.getExcelCellData();

            if (suiteConfig.getRunMode().equalsIgnoreCase("YES")) {
                suiteResult = this.testInterface(suiteConfig, driver);
            } else {
                logger.info("接口 [" + suiteConfig.getInterfaceName() + "]" + "不进行测试");
                //测试跳过
                suiteResult.setData(TestResult.SKIP.toString());
            }
            suiteConfigList.get(i).setExcelCellData(suiteResult);
        }
        configData.setSuiteConfigList(suiteConfigList);
        //所有需要测试的接口都测试完毕，下面统一将结果输出到 Excel 的接口测试用例上
        excelUtils.writeSuiteResult(configData);
    }

    /**
     * 单独测试一个接口
     *
     * @param suiteConfig 接口配置数据
     * @param driver http 接口启动器
     *
     * @return 接口测试集合结果
     */
    private synchronized ExcelCellData testInterface(final SuiteConfig suiteConfig, HttpDriver driver) {
        //获取测试接口
        String interfaceName = suiteConfig.getInterfaceName();
        logger.info("准备测试接口：[" + interfaceName + "]");

        InterfaceData interfaceData = excelUtils.getInterfaceData(interfaceName);
        //如果没有接口数据，则无需测试，测试结果失败
        if (interfaceData == null) {
            //先获取测试接口的默认测试结果（默认为空或 true）
            ExcelCellData suiteResult = suiteConfig.getExcelCellData();
            suiteResult.setData(TestResult.FAIL.toString());
            return suiteResult;
        }

        //获取接口的信息、接口参数组合
        InterfaceInfo interfaceInfo = interfaceData.getInterfaceInfo();
        List<InterfaceParameters> interfaceParametersList = interfaceData.getParametersList();
        for (int j = 0; j < interfaceParametersList.size(); j++) {
            //对接口参数组合逐一进行测试
            ExcelWriteDatas writeResult = this.testInterfaceParameter(driver, interfaceInfo, interfaceParametersList.get(j));
            //这样写的目的，是为了将测试结果立即传递到 interfaceParametersList
            interfaceParametersList.get(j).setExcelWriteDatas(writeResult);
        }

        //先获取测试接口的默认测试结果（默认为空或 true）
        ExcelCellData suiteResult = suiteConfig.getExcelCellData();
        //判定接口测试总结果，如果某一组参数测试失败，则判定该接口测试失败
        if (suiteIsPass) {
            suiteResult.setData(TestResult.PASS.toString());
            logger.info("接口 [" + interfaceName + "] 全部参数测试通过");
        } else {
            suiteResult.setData(TestResult.FAIL.toString());
            logger.info("接口 [" + interfaceName + "] 测试失败，详情请查看日志和测试用例文件：[" + excelPath + "]");
        }

        interfaceData.setParametersList(interfaceParametersList);
        //将接口具体的测试结果写入到 Excel 中
        excelUtils.writeInterfaceExpectResult(interfaceData);
        return suiteResult;
    }

    /**
     * 测试接口，使用一种参数组合
     *
     * @param driver http 定位器
     * @param interfaceInfo 接口信息
     * @param interfaceParameters 接口参数组合
     *
     * @return 测试结果
     */
    private synchronized ExcelWriteDatas testInterfaceParameter(HttpDriver driver, InterfaceInfo interfaceInfo, InterfaceParameters interfaceParameters) {
        String response = null;
        //获取接口测试参数
        String interfaceName = interfaceInfo.getName();
        ExcelWriteDatas writeResult = interfaceParameters.getExcelWriteDatas();

        MyURI myFullURI = driver.getFullURI(interfaceInfo, interfaceParameters);
        MyURI myPartURI = driver.getPartURI(interfaceInfo,interfaceParameters);

        ArrayList<MyURI> myURIS = new ArrayList<>(2);
        myURIS.add(myFullURI);
        if (!myFullURI.equals(myPartURI)) {
            myURIS.add(myPartURI);
        }

        //填写是否通过测试
        ExcelCellData isPass = writeResult.getWriteDatasMap().get(InterfaceParameters.IS_PASS);
        for (MyURI myURI : myURIS) {
            //将本次请求的完整地址写入到 Excel 中
            writeResult.getWriteDatasMap().get(InterfaceParameters.FULL_URL).setData(myURI.getUri().toString());
            //发起请求，并返回响应数据
            response = driver.sendRequestAndGetResponse(myURI);
            //填写实际测试结果
            writeResult.getWriteDatasMap().get(InterfaceParameters.ACTUAL).setData(response);

            //如果接口返回的状态不是200，则发生异常，响应数据为空
            if (response != null) {
                //如果响应的数据匹配预期结果，则判断测试成功
                if (StringUtils.isMatchPart(response, interfaceParameters.getExpectResult())) {
                    isPass.setData(TestResult.PASS.toString());
                    logger.info("接口 [" + interfaceName + "] 本次测试通过，参数列表：" + interfaceParameters);
                } else {
                    isPass.setData(TestResult.FAIL.toString());
                    suiteIsPass = false;
                    logger.error("接口 [" + interfaceName + "] 本次测试失败，响应数据与预期结果不符合，参数列表：" + interfaceParameters);
                }
            } else {
                //接口响应出错，并当做测试失败
                isPass.setData(TestResult.FAIL.toString());
                suiteIsPass = false;
                logger.error("接口 [" + interfaceName + "] 本次测试失败，响应出错，参数列表：" + interfaceParameters);
            }
        }

        //将测试数据，设置到总体的 InterfaceData 中，跳过中间获取的数据值
        writeResult.getWriteDatasMap().put(InterfaceParameters.IS_PASS, isPass);

        //如果测试结果通过，且提取公共参数单元格的值不为空
        String publicParameters = interfaceParameters.getExtractedPublicParameters();
        if (isPass.getData().equals(TestResult.PASS.toString())
                && StringUtils.isNotEmpty(publicParameters)
                ) {
            //提取公共参数
            this.setParameters(response, publicParameters);
        }
        return writeResult;
    }

    /**
     * 对接口响应数据，提取公共参数
     *
     * @param response 响应参数
     * @param publicParameters 提取参数的字符串
     *
     */
    private synchronized void setParameters(String response, String publicParameters) {
        String[] splits = StringUtils.getSplitString(publicParameters, "\\,");
        Map<String, String> publicParametersMap = new HashMap<>();
        logger.info("正在提取公共参数：[" + publicParameters + "]");
        //提取公共参数，并写入到【公共参数】sheet 里
        for (int i = 0; i < splits.length; i++) {
            String regex = "\"" + splits[i] + "\":(\"[a-zA-Z0-9_\\-]+\"|[a-zA-Z0-9_\\-]+)";
            String publicParam = StringUtils.getMatchString(response, regex);
            if (StringUtils.isNotEmpty(publicParam)) {
                //对匹配的字符串进行处理，去掉 key 值和一些标点符号
                publicParam = publicParam.replaceFirst(splits[i],"");
                publicParam = publicParam.replaceAll("[^a-zA-Z0-9_\\--\\-]+", "");
            }
            publicParametersMap.put(splits[i], publicParam);
        }
        if (publicParametersMap != null) {
            excelUtils.setPublicParameters(publicParametersMap);
        } else {
            logger.error("写入公共参数出错");
        }
    }

}


<|start_filename|>src/main/java/com/utils/FileUtils.java<|end_filename|>
package com.utils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件封装类，输出日志类
 * 
 * @version 1.1
 * 
 */

public class FileUtils {
	
	private static Logger logger = LogManager.getLogger();

	//构造函数，创建此对象，无参数
	public FileUtils() {
		
	}

	/**
	 * 将读取文件的 FileInputStream 提取出来，单独一个方法
	 * 
	 * @param fileFullPath 文件路径
	 * 
	 * @return FileInputStream 对象
	 */
	public FileInputStream setFileInputStream(String fileFullPath) {
		//创建一个 File 对象，设置默认值为空
		File file = null;
		//创建一个 FileInputStream 对象，用来读取文件
        FileInputStream fis = null;
		try {
        	file = new File(fileFullPath);    		//读取完成的文件路径
            fis = new FileInputStream(file);    	//读取所在文件路径的文件
            logger.info("构建 FileInputStream 成功");
		} catch (IOException e) {
			logger.error("构建 FileInputStream 失败", e);
		}
		return fis;
	}

	/**
	 * 读取文件内容
	 *
	 * @param fileFullPath 文件路径
	 *
	 * @return File 文本
	 */
	public static List<String> readFile(String fileFullPath) {
		List<String> stringList = new ArrayList<>();

		File keyFile = new File(fileFullPath);
		String readKey = null;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(keyFile)));
			while ((readKey = br.readLine())!= null) {
				stringList.add(readKey);
			}
			br.close();
		} catch (FileNotFoundException e) {
			logger.error("找不到文件:" + fileFullPath + "\n", e);
		} catch (IOException e) {
			logger.error("读取文件[" + fileFullPath + "]，发生异常\n", e);
		}
		logger.info("读取到的内容有：" + stringList);
		return stringList;
	}

	/**
	 * 将字符串写入到指定文件中
	 *
	 * @param content 写入内容
	 * @param filePath 文件路径
	 */
	public static void writeFile(String content, String filePath) {
		//先创建文件，再写入
		createFile(filePath);
		FileWriter fileWriter;
		try {
			fileWriter = new FileWriter(filePath, true);

			PrintWriter printWriter = new PrintWriter(fileWriter);
			printWriter.println(content);
			printWriter.println("\n");
			printWriter.flush();
			fileWriter.flush();

			printWriter.close();
			fileWriter.close();

		} catch(IOException e) {
			logger.catching(e);
		}
	}

	/**
	 * 如果文件不存在，先创建
	 * 
	 * @param fileFullName 文件路径
	 * 
	 */
	public static void createFile(String fileFullName) {
		File newFile = new File(fileFullName);
		try {
			if (!newFile.exists()) {
				File parent = newFile.getParentFile();
				if (!parent.exists()) {		//判断路径是否存在，不存在，则创建路径
					parent.mkdirs();
				}
				newFile.createNewFile();	//文件不存在，创建空白文件
				logger.debug("创建文件：" + fileFullName);
			}
			else {
				logger.debug("文件已存在");
			}
		} catch (IOException e) {
			logger.catching(e);
		}
	}
	
	/**
	 * 删除指定文件
	 * 
	 * @param filePath 文件路径
	 * 
	 */
	public static void deleteFile(String filePath) {
		File deleteFile = new File(filePath);
		if (deleteFile.exists()) {
			if (deleteFile.delete()) {
				logger.debug("删除文件 [" + filePath + "] 成功");
			} else {
				logger.debug("删除文件 [" + filePath + "] 失败");
			}
		} else {
			 logger.debug("文件 [" + filePath + "] 不存在");
		}
	}
	
	/**
	 * 获取指定类的完整路径
	 * 
	 * @param clazz 类的路径
	 * 
	 * @return 调用 Class 的路径
	 * 
	 */
	public static String getClassPath(Class<?> clazz) {
		return clazz.getCanonicalName().replaceAll("\\.", File.separator);
	}

	/**
	 * 以 src/开头的路径变为绝对路径
	 *
	 * @param simplePath 相对路径
	 * @return 绝对路径
	 */
	public static String getAbsolutePath(String simplePath) {
		if (simplePath.startsWith(File.separator + "src" + File.separator)) {
			simplePath = System.getProperty("user.dir") + simplePath;
		}
		return simplePath;
	}

}


<|start_filename|>src/main/java/com/MainTest.java<|end_filename|>
package com;

import com.engine.Excel_Engine;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 整个 jar 包（java 工程）的主类
 *
 */

public class MainTest {

    private static Logger logger = LogManager.getLogger();
    /**
     * 在运行 jar 包时，需提供 args 参数
     *
     * @param args 运行时，需提供的参数
     */
    public static void main(String[] args) {
        if (args.length == 0) {
            logger.error("请在 jar 文件后面添加接口测试用例文档的路径，如：java -jar /your/jar/path/apitest.jar /your/excel/api/test/file/path/api-test.xlsx");
        }
        for (int i = 0; i < args.length; i++) {
            Excel_Engine engine = new Excel_Engine(args[i]);
            engine.runTest();
        }
    }
}


<|start_filename|>src/main/java/com/interfaceData/InterfaceData.java<|end_filename|>
package com.interfaceData;

import java.util.List;

/**
 * 存储一个接口的所有数据，
 * 包括：路径、描述、参数名称、调用方法、参数、预期结果、实际测试结果
 *
 * @version 1.0
 */

public class InterfaceData {

    private InterfaceInfo interfaceInfo;
    private List<InterfaceParameters> parametersList;

    /**
     * 声明此类，一次性创建以下接口测试数据
     *
     * @param interfaceInfo 接口基本信息
     * @param parametersList 接口参数组合
     */
    public InterfaceData(InterfaceInfo interfaceInfo, List<InterfaceParameters> parametersList) {
        this.interfaceInfo = interfaceInfo;
        this.parametersList = parametersList;
    }

    public InterfaceInfo getInterfaceInfo() {
        return interfaceInfo;
    }

    public void setInterfaceInfo(InterfaceInfo interfaceInfo) {
        this.interfaceInfo = interfaceInfo;
    }

    public List<InterfaceParameters> getParametersList() {
        return parametersList;
    }

    public void setParametersList(List<InterfaceParameters> parametersList) {
        this.parametersList = parametersList;
    }

    @Override
    public String toString() {
        return "InterfaceData{" +
                "interfaceInfo=" + interfaceInfo +
                ", parametersList=" + parametersList +
                '}';
    }
}

<|start_filename|>src/main/java/com/http/InterfaceDriver.java<|end_filename|>
package com.http;

import org.apache.http.HttpResponse;

import java.util.Map;

/**
 * 接口测试启动器接口，方便扩展其他接口测试
 *
 * @version 1.0
 */

public interface InterfaceDriver {

    void setHeader(Map<String, String> headers);

    HttpResponse sendRequest(MyURI myURI);

    String getResponseData(HttpResponse response);

}


<|start_filename|>src/main/java/com/interfaceData/InterfaceInfo.java<|end_filename|>
package com.interfaceData;

import java.util.List;

/**
 * 接口的基本信息，
 * 包括：名称、路径、描述、参数
 *
 * @version 1.0
 */

public class InterfaceInfo {

    private String name;
    private String path;
    private String describe;
    //接口的参数名称
    private List<String> paramNames;

    /**
     * 声明类时，即时创建接口信息
     *
     * @param name 接口名称
     * @param path 接口路径
     * @param describe 接口用途描述
     * @param paramNames 参数列表
     */
    public InterfaceInfo(String name, String path, String describe, List<String> paramNames) {
        this.name = name;
        this.path = path;
        this.describe = describe;
        this.paramNames = paramNames;
    }

    public InterfaceInfo(String name, String path) {
        this.name = name;
        this.path = path;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }

    public List<String> getParamNames() {
        return paramNames;
    }

    public void setParamNames(List<String> paramNames) {
        this.paramNames = paramNames;
    }

    @Override
    public String toString() {
        return "Interface-\""+ name +"\"{" +
                "path='" + path + '\'' +
                ", describe='" + describe + '\'' +
                ", paramNames=" + paramNames +
                '}';
    }
}



<|start_filename|>src/main/java/com/http/HttpDriver.java<|end_filename|>
package com.http;

import com.interfaceData.*;
import com.utils.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 一个主 URl，一个 HttpDriver
 *
 * @version 1.0
 */

public class HttpDriver implements InterfaceDriver {

    private Logger logger = LogManager.getLogger();

    private final String scheme;
    private final String host;

    private CloseableHttpClient httpClient;
    private Map<String, String> headers;
    /**
     * 一个 host 配置一个 HttpDriver
     *
     * @param mainURL 接口协议和主 url 数据
     *
     */
    public HttpDriver(InterfaceMainURL mainURL) {
        //设置 http 协议和接口的主 url，不可变动
        this.scheme = mainURL.getScheme();
        this.host = mainURL.getHost();
    }

    /**
     * 使用指定参数组合一条 用于接口测试的主 url，保留空值的参数
     *
     * @param interfaceInfo 接口信息
     * @param parameters 参数
     *
     * @return 带指定参数的接口 url
     */
    public MyURI getFullURI(InterfaceInfo interfaceInfo, InterfaceParameters parameters) {
        MyURI myURI = null;

        URIBuilder uriBuilder = new URIBuilder()
                .setScheme(this.scheme)
                .setHost(this.host)
                .setPath(interfaceInfo.getPath());
        if (parameters.getParameters() == null) {
            try {
                myURI = new MyURI(parameters.getMethod(), uriBuilder.build());
                //logger.info("获取到接口的具体地址：" + myURI);
            } catch (URISyntaxException e) {
                logger.error("获取接口地址出错");
                logger.catching(e);
            }
            return myURI;
        }

        Set<String> paramNames = parameters.getParameters().keySet();
        Iterator<String> iterator = paramNames.iterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            String value = parameters.getParameters().get(key);
            //在这里控制空值传递
            uriBuilder.setParameter(key, value);
        }
        try {
            myURI = new MyURI(parameters.getMethod(), uriBuilder.build());
            //logger.info("获取到接口的具体地址：" + myURI);
        } catch (URISyntaxException e) {
            logger.error("获取接口地址出错");
            logger.catching(e);
        }
        return myURI;
    }

    /**
     * 使用指定参数组合一条 用于接口测试的主 url，不保留空值的参数
     *
     * @param interfaceInfo 接口信息
     * @param parameters 参数
     *
     * @return 带指定参数的接口 url
     */
    public MyURI getPartURI(InterfaceInfo interfaceInfo, InterfaceParameters parameters) {
        URIBuilder uriBuilder = new URIBuilder()
                .setScheme(this.scheme)
                .setHost(this.host)
                .setPath(interfaceInfo.getPath());
        Set<String> paramNames = parameters.getParameters().keySet();
        Iterator<String> iterator = paramNames.iterator();

        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = parameters.getParameters().get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            } else {
                uriBuilder.setParameter(key, value);
            }
        }

        MyURI myURI = null;
        try {
            myURI = new MyURI(parameters.getMethod(), uriBuilder.build());
            //logger.info("获取到接口的具体地址：" + myURI);
        } catch (URISyntaxException e) {
            logger.error("获取接口地址出错");
            logger.catching(e);
        }
        return myURI;
    }

    /**
     * 获取接口测试的主 url
     *
     * @param uriRequestMethod 接口调用方法
     * @param uri 接口地址
     *
     * @return 带指定参数的接口 url
     */
    public MyURI getURI(String uriRequestMethod, String uri) {
        MyURI myURI = null;
        try {
            myURI = new MyURI(uriRequestMethod, new URIBuilder(uri).build());
            //logger.info("获取到接口的具体地址：" + myURI);
        } catch (URISyntaxException e) {
            logger.error("获取接口地址出错");
            logger.catching(e);
        }
        return myURI;
    }

    /**
     * 设置请求头信息，同样可以设置 cookies
     *
     * @param headers 请求头
     */
    @Override
    public void setHeader(Map<String, String> headers) {
        this.headers = headers;
    }

    /**
     * 发送请求，自动判断请求方法
     *
     * @param myURI 带请求方法的 URI
     *
     * @return 响应数据
     */
    @Override
    public HttpResponse sendRequest(MyURI myURI) {
        logger.debug("创建网络连接");
        httpClient = HttpClients.createDefault();
        URI uri = myURI.getUri();
        HttpUriRequest request;
        switch (myURI.getMethod()) {
            case HttpDelete.METHOD_NAME :
                request = new HttpDelete(uri);
                break;
            case HttpGet.METHOD_NAME :
                request = new HttpGet(uri);
                break;
            case HttpHead.METHOD_NAME :
                request = new HttpHead(uri);
                break;
            case HttpOptions.METHOD_NAME :
                request = new HttpOptions(uri);
                break;
            case HttpPatch.METHOD_NAME :
                request = new HttpPatch(uri);
                break;
            case HttpPost.METHOD_NAME :
                request = new HttpPost(uri);
                break;
            case HttpPut.METHOD_NAME :
                request = new HttpPut(uri);
                break;
            case HttpTrace.METHOD_NAME :
                request = new HttpTrace(uri);
                break;
            default:
                request = new HttpGet(uri);
                break;
        }
        //将手动设置的请求头一一设置到本次请求中
        if (headers.size() > 0) {
            Set<String> headerKeys = headers.keySet();
            for (String key : headerKeys) {
                request.addHeader(key, headers.get(key));
            }
        }
        logger.info("正在发起请求：" + myURI);
        HttpResponse response = null;
        try {
            response = httpClient.execute(request);
        } catch (IOException e) {
            logger.error("发起请求出错，正准备重新请求");
            logger.catching(e);
            //重新请求一次
            try {
                response = httpClient.execute(request);
            } catch (IOException e1) {
                logger.error("再一次发起请求出错，无法得到响应");
                logger.catching(e1);
            }
        }
        return response;
    }

    /**
     * 获取接口响应的数据
     *
     * @param response 接口响应
     *
     * @return 接口响应的数据
     */
    @Override
    public String getResponseData(HttpResponse response) {
        //如果响应数据为空，则是发起请求出错了，响应数据依然为空值
        if (response == null) {
            logger.error("接口响应数据为空");
            return null;
        }

        int statusCode = response.getStatusLine().getStatusCode();
        logger.info("接口返回状态码：[" + statusCode + "]");

        //如果状态码不是200，返回数据为空
        if (statusCode != 200) {
            return "\"HTTP Response StatusCode\":" + statusCode;
        }

        HttpEntity entity = response.getEntity();
        //StringBuilder stringBuilder = new StringBuilder();
        String responseData = null;
        try {
/*            if (entity != null) {
                InputStream inputStream = entity.getContent();
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

                    String line = null;
                    try {
                        while ((line = reader.readLine())!= null) {
                            stringBuilder.append(line + "\n");
                        }
                    } catch (IOException e) {
                        logger.catching(e);
                    }
                } finally {
                    inputStream.close();
                }
            }
            logger.info("接口响应数据：" + stringBuilder.toString());*/
            responseData = EntityUtils.toString(entity, "UTF-8");
            logger.info("接口响应数据：" + responseData);
        } catch (IOException e) {
            logger.error("获取接口响应数据异常");
            logger.catching(e);
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                logger.info("网络连接关闭异常");
                logger.catching(e);
            }
        }
        return responseData;
    }

    /**
     * 对接口使用指定参数组合发起请求，获取响应数据
     *
     * @param myURI 带参数的 url
     *
     * @return 接口响应数据
     */
    public String sendRequestAndGetResponse(MyURI myURI) {
        return getResponseData(this.sendRequest(myURI));
    }

}