接口自动化测试 – Java+TestNG 测试 Restful Web Service
来源:互联网 发布:舞台剧网络营销策划书 编辑:程序博客网 时间:2024/05/01 19:44
本文主要介绍如何用Java针对Restful web service 做接口自动化测试(数据驱动),相比UI自动化,接口自动化稳定性可靠性高,实施难易程度低,做自动化性价比高。所用到的工具或类库有 TestNG, Apache POI, Jayway rest-assured,Skyscreamer - JSONassert
简介:
思想是数据驱动测试,用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.java
1 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[]> 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 order107 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 @AfterTest171 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 strings105 // like <<id>>106 String[] tokens = tokenize_template(template);107 108 // Repeatedly perform replacements with data from record until no109 // replacements are found110 // If a replacement's result is an empty string, it will not throw an111 // 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 line142 // 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 suffix149 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 token155 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 call160 // host and call suffix161 call_string = call_host + call_suffix;162 163 // Remaining lines will contain headers, until the read line is164 // empty165 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 body177 // from them178 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
- APP接口自动化测试JAVA+TestNG小实例
- 通过junit/TestNG+java简单实现接口的自动化测试
- Eclipse+TestNG搭建接口自动化测试框架
- Eclipse+TestNG搭建接口自动化测试框架
- APP接口自动化测试JAVA+TestNG之HTTP接口测试实例
- web自动化测试框架(Selenium+TestNG)
- Web自动化测试之Webdriver+TestNG
- (JAVA+TESTNG 二)Eclipse+TestNG搭建接口自动化测试框架
- (JAVA+TESTNG 三)Eclipse+TestNG搭建接口自动化测试框架
- 自动化测试--TestNG
- 自动化测试框架Selenium+java+TestNG
- Selenium+Java+Appium+TestNg环境搭建——Web自动化测试与HTML5测试
- Selenium+Java+Appium+TestNg环境搭建——Web自动化测试与HTML5测试(2)
- Selenium+Java+Appium+TestNg环境搭建——Web自动化测试与HTML5测试(3)
- input输入子系统详解
- hihocoder #1361 : Playfair密码表
- 剑指-包含min函数的栈
- list_entry头文件无法使用
- http://blog.csdn.net/uncle_guo/article/details/42575013
- 接口自动化测试 – Java+TestNG 测试 Restful Web Service
- QR分解之HouseHolder变换
- C++: string的大小写转换
- mysql索引总结----mysql 索引类型以及创建
- |Vijos|贪心|P1444 区间
- Android GC Log
- router get post all
- CSU - 1160 十进制-十六进制
- spring注解