接口自动化测试 – Java+TestNG 测试 Restful Web Service
2016-08-25 15:33
603 查看
本文主要介绍如何用Java针对Restful web service 做接口自动化测试(数据驱动),相比UI自动化,接口自动化稳定性可靠性高,实施难易程度低,做自动化性价比高。所用到的工具或类库有 TestNG, Apache POI, Jayway rest-assured,Skyscreamer - JSONassert
@BeforeTest 读取Excel (WorkBook) 的 ‘Input’ 和 ‘Baseline’ sheet
并且新建‘Output’, ‘Comparison’, ‘Result’ 三个空sheet
读取http_request_template.txt 内容转成string
@DataProvider (name = "WorkBookData")
TestNG的DataProvider, 首先用DataReader构造函数,读取Excel中Input的数据,放入HashMap<String, RecordHandler>, Map的key值就是test case的ID,value是RecordHandler对象,此对象中一个重要的成员属性就是input sheet里面 column和value 的键值对,遍历Map将test case ID 与 test case的value 即input sheet前两列的值放入List<Object[]> ,最后返回List的迭代器iterator
(为了循环调用@Test方法)
@Test (dataProvider = "WorkBookData", description = "ReqGenTest")
测试方法,由DataProvider提供数据,首先根据ID去取myInputData里的RecordHandler, 由它和template 去生成request, 然后执行request 返回response,这些工作都是由HTTPReqGen.java这个类完成的,借助com.jayway.restassured, 返回的response是一个JSON体,通过org.skyscreamer.jsonassert.JSONCompare 与Baseline中事先填好的期望结果(同样也是JSON格式)进行比较,
根据结果是Pass还是Fail, 都会相应的往Excel里的相应Sheet写结果。
@AfterTest
写入统计的一些数据
关闭文件流
View Code
DataReader
View Code
HTTPReqGen
View Code
RecordHandler
View Code
其它不重要的类不一一列出来了。
pom.xml
View Code
运行是通过TestNG的xml文件来执行的, 里面配置了Parameter “workBook” 的路径
TestNG的运行结果都是Pass, 但事实上里面有case是Fail的,我只是借助TestNG来运行,我并没有在@Test方法里加断言Assert, 所以这里不会Fail, 我的目的是完全用Excel来管理维护测试数据以及测试结果,做到数据脚本完全分离。
Output sheet
Comparison sheet
Result sheet
当然 你也可以把maven工程打成一个可执行jar来运行,不过需要增加一个main函数作为入口,xml测试文件通过参数传递进去,另外还需要在pom里配置一些插件,像maven-jar-plugin。
如果你还需要做back-end DB check,你可以在Input里再增加几列,你要查询的表,字段,Baseline里也相应的加上期望结果,这里就不再赘述了。
简介:
思想是数据驱动测试,用Excel来管理数据,‘Input’ Sheet中存放输入数据,读取数据后拼成request 调用service, 拿到response后写入 ‘Output’ Sheet 即实际结果, ‘Baseline’为基线(期望结果)用来和实际结果对比的,‘Comparison’ Sheet里存放的是对比结果不一致的记录,‘Result’ Sheet 是一个简单的结果报告。Maven工程目录结构:
详细介绍
核心就一个测试类HTTPReqGenTest.java 由四部分组成@BeforeTest 读取Excel (WorkBook) 的 ‘Input’ 和 ‘Baseline’ sheet
并且新建‘Output’, ‘Comparison’, ‘Result’ 三个空sheet
读取http_request_template.txt 内容转成string
@DataProvider (name = "WorkBookData")
TestNG的DataProvider, 首先用DataReader构造函数,读取Excel中Input的数据,放入HashMap<String, RecordHandler>, Map的key值就是test case的ID,value是RecordHandler对象,此对象中一个重要的成员属性就是input sheet里面 column和value 的键值对,遍历Map将test case ID 与 test case的value 即input sheet前两列的值放入List<Object[]> ,最后返回List的迭代器iterator
(为了循环调用@Test方法)
@Test (dataProvider = "WorkBookData", description = "ReqGenTest")
测试方法,由DataProvider提供数据,首先根据ID去取myInputData里的RecordHandler, 由它和template 去生成request, 然后执行request 返回response,这些工作都是由HTTPReqGen.java这个类完成的,借助com.jayway.restassured, 返回的response是一个JSON体,通过org.skyscreamer.jsonassert.JSONCompare 与Baseline中事先填好的期望结果(同样也是JSON格式)进行比较,
根据结果是Pass还是Fail, 都会相应的往Excel里的相应Sheet写结果。
@AfterTest
写入统计的一些数据
关闭文件流
实现代码:
HTTPReqGenTest.java1 package com.demo.qa.rest_api_test; 2 3 import java.io.FileInputStream; 4 import java.io.FileNotFoundException; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.nio.charset.Charset; 9 import java.text.SimpleDateFormat; 10 import java.util.ArrayList; 11 import java.util.Date; 12 import java.util.Iterator; 13 import java.util.List; 14 import java.util.Map; 15 16 import org.apache.commons.io.IOUtils; 17 import org.apache.poi.xssf.usermodel.XSSFSheet; 18 import org.apache.poi.xssf.usermodel.XSSFWorkbook; 19 import org.json.JSONException; 20 import org.skyscreamer.jsonassert.JSONCompare; 21 import org.skyscreamer.jsonassert.JSONCompareMode; 22 import org.skyscreamer.jsonassert.JSONCompareResult; 23 import org.testng.Assert; 24 import org.testng.ITest; 25 import org.testng.ITestContext; 26 import org.testng.annotations.AfterTest; 27 import org.testng.annotations.BeforeTest; 28 import org.testng.annotations.DataProvider; 29 import org.testng.annotations.Parameters; 30 import org.testng.annotations.Test; 31 32 import com.demo.qa.utils.DataReader; 33 import com.demo.qa.utils.DataWriter; 34 import com.demo.qa.utils.HTTPReqGen; 35 import com.demo.qa.utils.RecordHandler; 36 import com.demo.qa.utils.SheetUtils; 37 import com.demo.qa.utils.Utils; 38 import com.jayway.restassured.response.Response; 39 40 public class HTTPReqGenTest implements ITest { 41 42 private Response response; 43 private DataReader myInputData; 44 private DataReader myBaselineData; 45 private String template; 46 47 public String getTestName() { 48 return "API Test"; 49 } 50 51 String filePath = ""; 52 53 XSSFWorkbook wb = null; 54 XSSFSheet inputSheet = null; 55 XSSFSheet baselineSheet = null; 56 XSSFSheet outputSheet = null; 57 XSSFSheet comparsionSheet = null; 58 XSSFSheet resultSheet = null; 59 60 private double totalcase = 0; 61 private double failedcase = 0; 62 private String startTime = ""; 63 private String endTime = ""; 64 65 66 @BeforeTest 67 @Parameters("workBook") 68 public void setup(String path) { 69 filePath = path; 70 try { 71 wb = new XSSFWorkbook(new FileInputStream(filePath)); 72 } catch (FileNotFoundException e) { 73 e.printStackTrace(); 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 inputSheet = wb.getSheet("Input"); 78 baselineSheet = wb.getSheet("Baseline"); 79 80 SheetUtils.removeSheetByName(wb, "Output"); 81 SheetUtils.removeSheetByName(wb, "Comparison"); 82 SheetUtils.removeSheetByName(wb, "Result"); 83 outputSheet = wb.createSheet("Output"); 84 comparsionSheet = wb.createSheet("Comparison"); 85 resultSheet = wb.createSheet("Result"); 86 87 try { 88 InputStream is = HTTPReqGenTest.class.getClassLoader().getResourceAsStream("http_request_template.txt"); 89 template = IOUtils.toString(is, Charset.defaultCharset()); 90 } catch (Exception e) { 91 Assert.fail("Problem fetching data from input file:" + e.getMessage()); 92 } 93 94 SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 95 startTime = sf.format(new Date()); 96 } 97 98 @DataProvider(name = "WorkBookData") 99 protected Iterator<Object[]> testProvider(ITestContext context) { 100 101 List<Object[]&g 24000 t; test_IDs = new ArrayList<Object[]>(); 102 103 myInputData = new DataReader(inputSheet, true, true, 0); 104 Map<String, RecordHandler> myInput = myInputData.get_map(); 105 106 // sort map in order so that test cases ran in a fixed order 107 Map<String, RecordHandler> sortmap = Utils.sortmap(myInput); 108 109 for (Map.Entry<String, RecordHandler> entry : sortmap.entrySet()) { 110 String test_ID = entry.getKey(); 111 String test_case = entry.getValue().get("TestCase"); 112 if (!test_ID.equals("") && !test_case.equals("")) { 113 test_IDs.add(new Object[] { test_ID, test_case }); 114 } 115 totalcase++; 116 } 117 118 myBaselineData = new DataReader(baselineSheet, true, true, 0); 119 120 return test_IDs.iterator(); 121 } 122 123 @Test(dataProvider = "WorkBookData", description = "ReqGenTest") 124 public void api_test(String ID, String test_case) { 125 126 HTTPReqGen myReqGen = new HTTPReqGen(); 127 128 try { 129 myReqGen.generate_request(template, myInputData.get_record(ID)); 130 response = myReqGen.perform_request(); 131 } catch (Exception e) { 132 Assert.fail("Problem using HTTPRequestGenerator to generate response: " + e.getMessage()); 133 } 134 135 String baseline_message = myBaselineData.get_record(ID).get("Response"); 136 137 if (response.statusCode() == 200) 138 try { 139 DataWriter.writeData(outputSheet, response.asString(), ID, test_case); 140 141 JSONCompareResult result = JSONCompare.compareJSON(baseline_message, response.asString(), JSONCompareMode.NON_EXTENSIBLE); 142 if (!result.passed()) { 143 DataWriter.writeData(comparsionSheet, result, ID, test_case); 144 DataWriter.writeData(resultSheet, "false", ID, test_case, 0); 145 DataWriter.writeData(outputSheet); 146 failedcase++; 147 } else { 148 DataWriter.writeData(resultSheet, "true", ID, test_case, 0); 149 } 150 } catch (JSONException e) { 151 DataWriter.writeData(comparsionSheet, "", "Problem to assert Response and baseline messages: "+e.getMessage(), ID, test_case); 152 DataWriter.writeData(resultSheet, "error", ID, test_case, 0); 153 failedcase++; 154 Assert.fail("Problem to assert Response and baseline messages: " + e.getMessage()); 155 } 156 else { 157 DataWriter.writeData(outputSheet, response.statusLine(), ID, test_case); 158 159 if (baseline_message.equals(response.statusLine())) { 160 DataWriter.writeData(resultSheet, "true", ID, test_case, 0); 161 } else { 162 DataWriter.writeData(comparsionSheet, baseline_message, response.statusLine(), ID, test_case); 163 DataWriter.writeData(resultSheet, "false", ID, test_case, 0); 164 DataWriter.writeData(outputSheet); 165 failedcase++; 166 } 167 } 168 } 169 170 @AfterTest 171 public void teardown() { 172 SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 173 endTime = sf.format(new Date()); 174 DataWriter.writeData(resultSheet, totalcase, failedcase, startTime, endTime); 175 176 try { 177 FileOutputStream fileOutputStream = new FileOutputStream(filePath); 178 wb.write(fileOutputStream); 179 fileOutputStream.close(); 180 } catch (FileNotFoundException e) { 181 e.printStackTrace(); 182 } catch (IOException e) { 183 e.printStackTrace(); 184 } 185 } 186 }
View Code
DataReader
1 package com.demo.qa.utils; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 7 import org.apache.poi.ss.usermodel.Cell; 8 import org.apache.poi.xssf.usermodel.XSSFCell; 9 import org.apache.poi.xssf.usermodel.XSSFRow; 10 import org.apache.poi.xssf.usermodel.XSSFSheet; 11 import org.slf4j.Logger; 12 import org.slf4j.LoggerFactory; 13 14 /** 15 * Class that read data from XSSF sheet 16 * 17 */ 18 public class DataReader { 19 20 protected static final Logger logger = LoggerFactory.getLogger(DataReader.class); 21 22 private HashMap<String, RecordHandler> map = new HashMap<String, RecordHandler>(); 23 24 private Boolean byColumnName = false; 25 private Boolean byRowKey = false; 26 private List<String> headers = new ArrayList<String>(); 27 28 private Integer size = 0; 29 30 public DataReader() { 31 } 32 33 /** 34 * Primary constructor. Uses Apache POI XSSF to pull data from given excel workbook sheet. Data is stored in a 35 * structure depending on the options from other parameters. 36 * 37 * @param sheet Given excel sheet. 38 * @param has_headers Boolean used to specify if the data has a header or not. The headers will be used as field keys. 39 * @param has_key_column Boolean used to specify if the data has a column that should be used for record keys. 40 * @param key_column Integer used to specify the key column for record keys. 41 */ 42 public DataReader(XSSFSheet sheet, Boolean has_headers, Boolean has_key_column, Integer key_column) { 43 44 XSSFRow myRow = null; 45 HashMap<String, String> myList; 46 size = 0; 47 48 this.byColumnName = has_headers; 49 this.byRowKey = has_key_column; 50 51 try { 52 53 if(byColumnName) { 54 myRow = sheet.getRow(0); 55 for(Cell cell: myRow) { 56 headers.add(cell.getStringCellValue()); 57 } 58 size = 1; 59 } 60 61 for(; (myRow = sheet.getRow(size)) != null; size++ ) { 62 63 myList = new HashMap<String, String>(); 64 65 if(byColumnName) { 66 for(int col = 0; col < headers.size(); col++ ) { 67 if(col < myRow.getLastCellNum()) { 68 myList.put(headers.get(col), getSheetCellValue(myRow.getCell(col))); // myRow.getCell(col).getStringCellValue()); 69 } else { 70 myList.put(headers.get(col), ""); 71 } 72 } 73 } else { 74 for(int col = 0; col < myRow.getLastCellNum(); col++ ) { 75 myList.put(Integer.toString(col), getSheetCellValue(myRow.getCell(col))); 76 } 77 } 78 79 if(byRowKey) { 80 if(myList.size() == 2 && key_column == 0) { 81 map.put(getSheetCellValue(myRow.getCell(key_column)), new RecordHandler(myList.get(1))); 82 } else if(myList.size() == 2 && key_column == 1) { 83 map.put(getSheetCellValue(myRow.getCell(key_column)), new RecordHandler(myList.get(0))); 84 } else { 85 map.put(getSheetCellValue(myRow.getCell(key_column)), new RecordHandler(myList)); 86 } 87 } else { 88 map.put(Integer.toString(size), new RecordHandler(myList)); 89 } 90 } 91 92 } catch (Exception e) { 93 logger.error("Exception while loading data from Excel sheet:"+e.getMessage()); 94 } 95 } 96 97 /** 98 * Utility method used for getting an excel cell value. Cell's type is switched to String before accessing. 99 * 100 * @param cell Given excel cell. 101 */ 102 private String getSheetCellValue(XSSFCell cell) { 103 104 String value = ""; 105 106 try { 107 cell.setCellType(Cell.CELL_TYPE_STRING); 108 value = cell.getStringCellValue(); 109 } catch(NullPointerException npe) { 110 return ""; 111 } 112 113 return value; 114 } 115 116 /** 117 * Returns entire HashMap containing this class's data. 118 * 119 * @return HashMap<String, RecordHandler>, map of ID-Record data. 120 */ 121 public HashMap<String, RecordHandler> get_map() { 122 123 return map; 124 } 125 126 127 /** 128 * Gets an entire record. 129 * 130 * @param record String key value for record to be returned. 131 * @return HashMap of key-value pairs representing the specified record. 132 */ 133 public RecordHandler get_record(String record) { 134 135 RecordHandler result = new RecordHandler(); 136 137 if(map.containsKey(record)) { 138 result = map.get(record); 139 } 140 141 return result; 142 } 143 144 }
View Code
HTTPReqGen
1 package com.demo.qa.utils; 2 3 import static com.jayway.restassured.RestAssured.given; 4 5 import java.io.BufferedReader; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.util.HashMap; 9 import java.util.Map; 10 11 import org.apache.commons.io.IOUtils; 12 import org.slf4j.Logger; 13 import org.slf4j.LoggerFactory; 14 15 import com.jayway.restassured.response.Response; 16 import com.jayway.restassured.specification.RequestSpecification; 17 18 /** 19 * Wrapper for RestAssured. Uses an HTTP request template and a single record housed in a RecordHandler object to 20 * generate and perform an HTTP requests. 21 * 22 */ 23 public class HTTPReqGen { 24 25 protected static final Logger logger = LoggerFactory.getLogger(HTTPReqGen.class); 26 27 private RequestSpecification reqSpec; 28 29 private String call_host = ""; 30 private String call_suffix = ""; 31 private String call_string = ""; 32 private String call_type = ""; 33 private String body = ""; 34 private Map<String, String> headers = new HashMap<String, String>(); 35 private HashMap<String, String> cookie_list = new HashMap<String, String>(); 36 37 public Map<String, String> getHeaders() { 38 return headers; 39 } 40 41 public String getCallString() { 42 return call_string; 43 } 44 45 /** 46 * Constructor. Initializes the RequestSpecification (relaxedHTTPSValidation avoids certificate errors). 47 * 48 */ 49 public HTTPReqGen() { 50 reqSpec = given().relaxedHTTPSValidation(); 51 } 52 53 public HTTPReqGen(String proxy) { 54 reqSpec = given().relaxedHTTPSValidation().proxy(proxy); 55 } 56 57 /** 58 * Pulls HashMap from given RecordHandler and calls primary generate_request method with it. 59 * 60 * @param template String, should contain the full template. 61 * @param record RecordHandler, the input data used to fill in replacement tags that exist in the template. 62 * @return this Reference to this class, primarily to allow request generation and performance in one line. 63 * @throws Exception 64 */ 65 public HTTPReqGen generate_request(String template, RecordHandler record) throws Exception { 66 67 return generate_request(template, (HashMap<String, String>) record.get_map()); 68 } 69 70 /** 71 * Generates request data, using input record to fill in the template and then parse out relevant data. To fill in the 72 * template, identifies tags surrounded by << and >> and uses the text from the corresponding fields in the 73 * RecordHandler to replace them. The replacement is recursive, so tags may also exist in the fields of the 74 * RecordHandler so long as they are also represented by the RecordHandler and do not form an endless loop. 75 * After filling in the template, parses the resulting string in preparation for performing the HTTP request. Expects the 76 * the string to be in the following format: 77 * 78 * <<call_type>> <<call_suffix>> 79 * Host: <<root_host_name>> 80 * <<header1_name>>:<<header1_value>> 81 * ... 82 * <<headerN_name>>: <<headerN_value>> 83 * 84 * <<body_text>> 85 * 86 * <<call_type>> must be GET, PUT, POST, or DELETE. <<call_suffix>> must be a string with no spaces. It is appended to 87 * <<root_host_name>> to form the complete call string. After a single blank line is encountered, the rest of the file 88 * is used as the body of text for PUT and POST calls. This function also expects the Record Handler to include a field 89 * named "VPID" containing a unique record identifier for debugging purposes. 90 * 91 * @param template String, should contain the full template. 92 * @param record RecordHandler, the input data used to fill in replacement tags that exist in the template. 93 * @return this Reference to this class, primarily to allow request generation and performance in one line. 94 * @throws Exception 95 */ 96 public HTTPReqGen generate_request(String template, HashMap<String, String> record) throws Exception { 97 98 String filled_template = ""; 99 Boolean found_replacement = true; 100 headers.clear(); 101 102 try { 103 104 // Splits template into tokens, separating out the replacement strings 105 // like <<id>> 106 String[] tokens = tokenize_template(template); 107 108 // Repeatedly perform replacements with data from record until no 109 // replacements are found 110 // If a replacement's result is an empty string, it will not throw an 111 // error (but will throw one if there is no column for that result) 112 while(found_replacement) { 113 found_replacement = false; 114 filled_template = ""; 115 116 for(String item: tokens) { 117 118 if(item.startsWith("<<") && item.endsWith(">>")) { 119 found_replacement = true; 120 item = item.substring(2, item.length() - 2); 121 122 if( !record.containsKey(item)) { 123 logger.error("Template contained replacement string whose value did not exist in input record:[" + item + "]"); 124 } 125 126 item = record.get(item); 127 } 128 129 filled_template += item; 130 } 131 132 tokens = tokenize_template(filled_template); 133 } 134 135 } catch (Exception e) { 136 logger.error("Problem performing replacements from template: ", e); 137 } 138 139 try { 140 141 // Feed filled template into BufferedReader so that we can read it line 142 // by line. 143 InputStream stream = IOUtils.toInputStream(filled_template, "UTF-8"); 144 BufferedReader in = new BufferedReader(new InputStreamReader(stream)); 145 String line = ""; 146 String[] line_tokens; 147 148 // First line should always be call type followed by call suffix 149 line = in.readLine(); 150 line_tokens = line.split(" "); 151 call_type = line_tokens[0]; 152 call_suffix = line_tokens[1]; 153 154 // Second line should contain the host as it's second token 155 line = in.readLine(); 156 line_tokens = line.split(" "); 157 call_host = line_tokens[1]; 158 159 // Full call string for RestAssured will be concatenation of call 160 // host and call suffix 161 call_string = call_host + call_suffix; 162 163 // Remaining lines will contain headers, until the read line is 164 // empty 165 line = in.readLine(); 166 while(line != null && !line.equals("")) { 167 168 String lineP1 = line.substring(0, line.indexOf(":")).trim(); 169 String lineP2 = line.substring(line.indexOf(" "), line.length()).trim(); 170 171 headers.put(lineP1, lineP2); 172 173 line = in.readLine(); 174 } 175 176 // If read line is empty, but next line(s) have data, create body 177 // from them 178 if(line != null && line.equals("")) { 179 body = ""; 180 while( (line = in.readLine()) != null && !line.equals("")) { 181 body += line; 182 } 183 } 184 185 } catch(Exception e) { 186 logger.error("Problem setting request values from template: ", e); 187 } 188 189 return this; 190 } 191 192 /** 193 * Performs the request using the stored request data and then returns the response. 194 * 195 * @return response Response, will contain entire response (response string and status code). 196 */ 197 public Response perform_request() throws Exception { 198 199 Response response = null; 200 201 try { 202 203 for(Map.Entry<String, String> entry: headers.entrySet()) { 204 reqSpec.header(entry.getKey(), entry.getValue()); 205 } 206 207 for(Map.Entry<String, String> entry: cookie_list.entrySet()) { 208 reqSpec.cookie(entry.getKey(), entry.getValue()); 209 } 210 211 switch(call_type) { 212 213 case "GET": { 214 response = reqSpec.get(call_string); 215 break; 216 } 217 case "POST": { 218 response = reqSpec.body(body).post(call_string); 219 break; 220 } 221 case "PUT": { 222 response = reqSpec.body(body).put(call_string); 223 break; 224 } 225 case "DELETE": { 226 response = reqSpec.delete(call_string); 227 break; 228 } 229 230 default: { 231 logger.error("Unknown call type: [" + call_type + "]"); 232 } 233 } 234 235 } catch (Exception e) { 236 logger.error("Problem performing request: ", e); 237 } 238 239 return response; 240 } 241 242 /** 243 * Splits a template string into tokens, separating out tokens that look like "<<key>>" 244 * 245 * @param template String, the template to be tokenized. 246 * @return list String[], contains the tokens from the template. 247 */ 248 private String[] tokenize_template(String template) { 249 return template.split("(?=[<]{2})|(?<=[>]{2})"); 250 } 251 252 }
View Code
RecordHandler
1 package com.demo.qa.utils; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 7 public class RecordHandler { 8 9 private enum RecordType { 10 VALUE, NAMED_MAP, INDEXED_LIST 11 } 12 13 private String single_value = ""; 14 private HashMap<String, String> named_value_map = new HashMap<String, String>(); 15 private List<String> indexed_value_list = new ArrayList<String>(); 16 private RecordType myType; 17 18 public RecordHandler() { 19 this(""); 20 } 21 22 public RecordHandler(String value) { 23 24 this.myType = RecordType.VALUE; 25 this.single_value = value; 26 27 } 28 29 public RecordHandler(HashMap<String, String> map) { 30 31 this.myType = RecordType.NAMED_MAP; 32 this.named_value_map = map; 33 34 } 35 36 public RecordHandler(List<String> list) { 37 38 this.myType = RecordType.INDEXED_LIST; 39 this.indexed_value_list = list; 40 41 } 42 43 public HashMap<String, String> get_map() { 44 return named_value_map; 45 } 46 47 public int size() { 48 int result = 0; 49 50 if(myType.equals(RecordType.VALUE)) { 51 result = 1; 52 } else if(myType.equals(RecordType.NAMED_MAP)) { 53 result = named_value_map.size(); 54 } else if(myType.equals(RecordType.INDEXED_LIST)) { 55 result = indexed_value_list.size(); 56 } 57 58 return result; 59 } 60 61 public String get() { 62 String result = ""; 63 64 if(myType.equals(RecordType.VALUE)) result = single_value; 65 else { 66 System.out.println("Called get() on wrong type:" + myType.toString()); 67 } 68 69 return result; 70 } 71 72 public String get(String key) { 73 String result = ""; 74 75 if(myType.equals(RecordType.NAMED_MAP)) result = named_value_map.get(key); 76 77 return result; 78 } 79 80 public String get(Integer index) { 81 String result = ""; 82 83 if(myType.equals(RecordType.INDEXED_LIST)) result = indexed_value_list.get(index); 84 85 return result; 86 } 87 88 public Boolean set(String value) { 89 Boolean result = false; 90 91 if(myType.equals(RecordType.VALUE)) { 92 this.single_value = value; 93 result = true; 94 } else if(myType.equals(RecordType.INDEXED_LIST)) { 95 this.indexed_value_list.add(value); 96 result = true; 97 } 98 99 return result; 100 } 101 102 public Boolean set(String key, String value) { 103 Boolean result = false; 104 105 if(myType.equals(RecordType.NAMED_MAP)) { 106 this.named_value_map.put(key, value); 107 result = true; 108 } 109 110 return result; 111 } 112 113 public Boolean set(Integer index, String value) { 114 Boolean result = false; 115 116 if(myType.equals(RecordType.INDEXED_LIST)) { 117 if(this.indexed_value_list.size() > index) this.indexed_value_list.set(index, value); 118 119 result = true; 120 } 121 122 return result; 123 } 124 125 public Boolean has(String value) { 126 Boolean result = false; 127 128 if(myType.equals(RecordType.VALUE) && this.single_value.equals(value)) { 129 result = true; 130 } else if(myType.equals(RecordType.NAMED_MAP) && this.named_value_map.containsKey(value)) { 131 result = true; 132 } else if(myType.equals(RecordType.INDEXED_LIST) && this.indexed_value_list.contains(value)) { 133 result = true; 134 } 135 136 return result; 137 } 138 139 public Boolean remove(String value) { 140 Boolean result = false; 141 142 if(myType.equals(RecordType.VALUE) && this.single_value.equals(value)) { 143 this.single_value = ""; 144 result = true; 145 } 146 if(myType.equals(RecordType.NAMED_MAP) && this.named_value_map.containsKey(value)) { 147 this.named_value_map.remove(value); 148 result = true; 149 } else if(myType.equals(RecordType.INDEXED_LIST) && this.indexed_value_list.contains(value)) { 150 this.indexed_value_list.remove(value); 151 result = true; 152 } 153 154 return result; 155 } 156 157 public Boolean remove(Integer index) { 158 Boolean result = false; 159 160 if(myType.equals(RecordType.INDEXED_LIST) && this.indexed_value_list.contains(index)) { 161 this.indexed_value_list.remove(index); 162 result = true; 163 } 164 165 return result; 166 } 167 168 }
View Code
其它不重要的类不一一列出来了。
pom.xml
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 3 <modelVersion>4.0.0</modelVersion> 4 5 <groupId>com.demo</groupId> 6 <artifactId>qa</artifactId> 7 <version>0.0.1-SNAPSHOT</version> 8 <name>Automation</name> 9 <description>Test project for Demo</description> 10 11 <properties> 12 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 13 </properties> 14 <build> 15 <plugins> 16 <plugin> 17 <artifactId>maven-compiler-plugin</artifactId> 18 <version>3.1</version> 19 <configuration> 20 <source>1.7</source> 21 <target>1.7</target> 22 </configuration> 23 </plugin> 24 <plugin> 25 <groupId>org.apache.maven.plugins</groupId> 26 <artifactId>maven-jar-plugin</artifactId> 27 </plugin> 28 <plugin> 29 <groupId>org.apache.maven.plugins</groupId> 30 <artifactId>maven-surefire-plugin</artifactId> 31 </plugin> 32 <plugin> 33 <artifactId>maven-dependency-plugin</artifactId> 34 </plugin> 35 </plugins> 36 <pluginManagement> 37 <plugins> 38 <plugin> 39 <groupId>org.apache.maven.plugins</groupId> 40 <artifactId>maven-jar-plugin</artifactId> 41 <version>2.5</version> 42 <executions> 43 <execution> 44 <id>default-jar</id> 45 <goals> 46 <goal>test-jar</goal> 47 </goals> 48 <configuration> 49 <archive> 50 <manifest> 51 <mainClass>com.demo.qa.utils.TestStartup</mainClass> 52 <addClasspath>true</addClasspath> 53 <classpathPrefix>lib/</classpathPrefix> 54 <useUniqueVersions>false</useUniqueVersions> 55 </manifest> 56 </archive> 57 </configuration> 58 </execution> 59 </executions> 60 </plugin> 61 <plugin> 62 <groupId>org.apache.maven.plugins</groupId> 63 <artifactId>maven-surefire-plugin</artifactId> 64 <version>2.17</version> 65 <configuration> 66 <skip>true</skip> 67 <suiteXmlFiles> 68 <suiteXmlFile>src\test\resources\HTTPReqGenTest.xml</suiteXmlFile> 69 </suiteXmlFiles> 70 </configuration> 71 </plugin> 72 <plugin> 73 <artifactId>maven-dependency-plugin</artifactId> 74 <version>2.8</version> 75 <executions> 76 <execution> 77 <id>default-cli</id> 78 <phase>package</phase> 79 <goals> 80 <goal>copy-dependencies</goal> 81 </goals> 82 <configuration> 83 <outputDirectory>${project.build.directory}/lib</outputDirectory> 84 </configuration> 85 </execution> 86 </executions> 87 </plugin> 88 <plugin> 89 <groupId>org.eclipse.m2e</groupId> 90 <artifactId>lifecycle-mapping</artifactId> 91 <version>1.0.0</version> 92 <configuration> 93 <lifecycleMappingMetadata> 94 <pluginExecutions> 95 <pluginExecution> 96 <pluginExecutionFilter> 97 <groupId>org.apache.maven.plugins</groupId> 98 <artifactId>maven-dependency-plugin</artifactId> 99 <versionRange>[1.0.0,)</versionRange> 100 <goals> 101 <goal>copy-dependencies</goal> 102 </goals> 103 </pluginExecutionFilter> 104 <action> 105 <execute /> 106 </action> 107 </pluginExecution> 108 </pluginExecutions> 109 </lifecycleMappingMetadata> 110 </configuration> 111 </plugin> 112 </plugins> 113 </pluginManagement> 114 </build> 115 <dependencies> 116 <dependency> 117 <groupId>org.apache.commons</groupId> 118 <artifactId>commons-lang3</artifactId> 119 <version>3.3.2</version> 120 </dependency> 121 122 <dependency> 123 <groupId>commons-io</groupId> 124 <artifactId>commons-io</artifactId> 125 <version>2.4</version> 126 </dependency> 127 <dependency> 128 <groupId>com.jayway.restassured</groupId> 129 <artifactId>rest-assured</artifactId> 130 <version>2.3.3</version> 131 </dependency> 132 <dependency> 133 <groupId>com.jayway.restassured</groupId> 134 <artifactId>json-path</artifactId> 135 <version>2.3.3</version> 136 </dependency> 137 <dependency> 138 <groupId>org.apache.poi</groupId> 139 <artifactId>poi</artifactId> 140 <version>3.10.1</version> 141 <exclusions> 142 <exclusion> 143 <artifactId>commons-codec</artifactId> 144 <groupId>commons-codec</groupId> 145 </exclusion> 146 </exclusions> 147 </dependency> 148 <dependency> 149 <groupId>org.testng</groupId> 150 <artifactId>testng</artifactId> 151 <version>6.8</version> 152 </dependency> 153 <dependency> 154 <groupId>commons-cli</groupId> 155 <artifactId>commons-cli</artifactId> 156 <version>1.2</version> 157 </dependency> 158 <dependency> 159 <groupId>org.apache.poi</groupId> 160 <artifactId>poi-ooxml</artifactId> 161 <version>3.10.1</version> 162 <exclusions> 163 <exclusion> 164 <artifactId>xml-apis</artifactId> 165 <groupId>xml-apis</groupId> 166 </exclusion> 167 </exclusions> 168 </dependency> 169 <dependency> 170 <groupId>org.skyscreamer</groupId> 171 <artifactId>jsonassert</artifactId> 172 <version>1.2.3</version> 173 </dependency> 174 <dependency> 175 <groupId>org.slf4j</groupId> 176 <artifactId>slf4j-api</artifactId> 177 <version>1.7.7</version> 178 </dependency> 179 <dependency> 180 <groupId>org.slf4j</groupId> 181 <artifactId>slf4j-simple</artifactId> 182 <version>1.7.6</version> 183 </dependency> 184 </dependencies> 185 </project>
View Code
运行是通过TestNG的xml文件来执行的, 里面配置了Parameter “workBook” 的路径
TestNG的运行结果都是Pass, 但事实上里面有case是Fail的,我只是借助TestNG来运行,我并没有在@Test方法里加断言Assert, 所以这里不会Fail, 我的目的是完全用Excel来管理维护测试数据以及测试结果,做到数据脚本完全分离。
Output sheet
Comparison sheet
Result sheet
当然 你也可以把maven工程打成一个可执行jar来运行,不过需要增加一个main函数作为入口,xml测试文件通过参数传递进去,另外还需要在pom里配置一些插件,像maven-jar-plugin。
如果你还需要做back-end DB check,你可以在Input里再增加几列,你要查询的表,字段,Baseline里也相应的加上期望结果,这里就不再赘述了。
相关文章推荐
- 接口自动化测试 – Java+TestNG 测试 Restful Web Service
- (转)接口自动化测试 – Java+TestNG 测试 Restful Web Service
- 接口自动化测试框架搭建 – Java+TestNG 测试Restful service
- 零成本实现接口自动化测试 – Java+TestNG 测试Restful service
- Java测试框架TestNG和JUnit对比
- 搭建Java测试环境的Eclipse+Maven+TestNG
- selenium+java+testNG+pageObject深入理解自动化测试框架
- 使用Java创建RESTful Web Service
- Selenium+Java+Appium+TestNg环境搭建——Web自动化测试与HTML5测试(4)
- 使用Java创建RESTful Web Service
- 根据测试用例的java源码自动生成TestNG的XML文件
- 基于CXF Java 搭建Web Service (Restful Web Service与基于SOAP的Web Service混合方案)
- Java测试新技术TestNG和高级概念 ---读后感
- java-xwiki restful接口简单测试
- Selenium+Java+Appium+TestNg环境搭建——Web自动化测试与HTML5测试(2)
- Spring Boot、Mybatis框架整合开发Java RESTful Web Service
- Java测试框架比较:TestNG VS JUnit 4
- 基于CXF Java 搭建Web Service (Restful Web Service与基于SOAP的Web Service混合方案)
- 基于CXF Java 搭建Web Service (Restful Web Service与基于SOAP的Web Service混合方案)
- 【转】基于CXF Java 搭建Web Service (Restful Web Service与基于SOAP的Web Service混合方案)