接口自动化测试 – 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里也相应的加上期望结果,这里就不再赘述了。

0 0