前言:
說在前邊。像我這種假期不出去浪,在這里乖乖寫文章研究代碼的人,絕壁不是因為愛學習,而是自己不知道去哪玩好,而且也不想玩游戲,看電視劇什么的,結果就無聊到看代碼了……
至於如何解讀代碼,請把它當做一門語言,況且它就是語言 ,計算機的,那就當做是外國人的語言,可以翻譯成漢語的!
例:system.out.print(" ") 翻譯:系統.輸出.打印(內容)。如是說!
本文介紹:
- Properties、Csv、Excel、JDBC
初級架構所需代碼之 參數配置與讀取——properties
一般代碼中需要讀取配置有幾種方式,在此一一舉例說明吧~
首先,比較常見的是讀取properties。一般,我們會將該文件存放在resource當中。
需要了解和使用的幾個主要方法:
1.getProperty ( String key),通過參數 key ,得到 key 所對應的 value。
2.load ( InputStream inStream),讀取配置,以供 getProperty ( String key) 來搜索。
3.setProperty ( String key, String value) ,set設置,也就是說設置key-value。
4.store ( OutputStream out, String comments),與 load 方法相反,該方法將 key-value 寫入到指定的文件中去。
5.clear (),清除所有裝載的key-value。該方法在基類中提供。
1和2搭配使用,3和4搭配使用。
實例解說
相比概念性的東西,伸手黨以及新手們更喜歡實例的東西來說明。
假如test.properties文件如下:
name=root pass=admin key=value
那么讀取和使用的方法如下(網上最多的文章是6種properties配置讀取方法,我這里推薦使用最常用的是這種):
Properties properties = new Properties(); //調用該方法,不要問為什么 InputStream in =null; //初始化輸入流 in = Obejct.class.getResourceAsStream("/config.properties"); //獲取該路徑下的properties數據 try { properties.load(in); //讀取數據 } catch (IOException e) { e.printStackTrace(); } properties.getProperty("pass"); //根據key獲取values
最后獲取的值就是pass對應的admin.(注意你的路徑是否正確,以免報錯nullPoint)
針對以上的例子,我們看到這樣的一句
Obejct.class.getResourceAsStream(path) //path表示你的properties路徑
為了安全起見,更推薦將Obejct改寫成你的當前類名,比如 readProperties.class.getResourceAsStream("路徑").
參數讀取與使用——csv
TestNg中csv數據讀取與使用,可以利用此結構遍歷csv中的數據,讓用例循環執行,直到數據全部讀取完畢。
我們新建add.csv到你的任意目錄中,比如我的放在這里:
1.直接上代碼,首先是csv的數據:
2.創建兩個類,一個是基類,用來實現n1+n2=r1這樣的功能:
public class Calculator { public Float add(Float num1,Float num2){ return num1+num2; } }
一個是工具類,用來讀取csv中的數據:

import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; public class CsvUtils implements Iterator<Object[]> { BufferedReader in; ArrayList<String> csvList=new ArrayList<String>(); int rowNum=0; //行數 int columnNum=0; //列數 int curRowNo=0; //當前行數 String columnName[]; //列名 /** * 在TestNG中由@DataProvider(dataProvider = "name")修飾的方法取csv數據時, * 調用此類構造方法(此方法會得到列名), * 返回給由@Test(dataProvider = "name")修飾的方法,如此 * 反復到數據讀完為止 * @param fileName 文件名 * @throws IOException */ public CsvUtils(String fileName) throws IOException{ File directory=new File("."); String path=".src.main.java.page.testdata."; //文件路徑 String absolutePath=directory.getCanonicalPath()+path.replaceAll("\\.", Matcher.quoteReplacement("\\"))+fileName; System.out.println(absolutePath); //打印路徑 File csv=new File(absolutePath); in=new BufferedReader(new FileReader(csv)); //讀取csv數據 while (in.ready()) { csvList.add(in.readLine()); this.rowNum++; } String[] str=csvList.get(0).split(","); this.columnNum=str.length; columnName=new String[columnNum]; //獲取列名 for (int i = 0; i < columnNum; i++) { columnName[i]=str[i]; } this.curRowNo++; } @Override public boolean hasNext() { // TODO Auto-generated method stub if(rowNum==0||curRowNo>=rowNum){ try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; }else{ return true; } } /** * 獲取一組參數,即一行數據 */ @Override public Object[] next() { // TODO Auto-generated method stub Map<String,String> s=new TreeMap<String,String>(); String csvCell[]=csvList.get(curRowNo).split(","); for(int i=0;i<this.columnNum;i++){ s.put(columnName[i], csvCell[i]); } Object[] d=new Object[1]; d[0]=s; this.curRowNo++; return d; } @Override public void remove() { // TODO Auto-generated method stub throw new UnsupportedOperationException("remove unsupported"); } }
3.實現類
import java.io.IOException; import java.util.Iterator; import java.util.Map; import core.utils.CsvUtils; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class CsvTest{ Calculator cal=new Calculator(); @DataProvider(name="num") public Iterator<Object[]> Numbers() throws IOException{ return (Iterator<Object[]>)new CsvUtils("add.csv"); } @Test(dataProvider="num") public void testAdd(Map<String, String> data){ float num1=Float.parseFloat(data.get("n1")); float num2=Float.parseFloat(data.get("n2")); float expectedResult=Float.parseFloat(data.get("r1")); Float actual=cal.add(num1, num2); Assert.assertEquals(actual, expectedResult); } }
4.執行結果:
不要光看,自己敲一遍就知道什么意思了!
Excel數據的讀取
對於已經寫好了腳本的同學,多數采用的是這種數驅的形式,這種形式很方便維護自己的用例,完全不用打開編譯器,改改數據就好。
但是,它也同樣存在弊端,比如數據量相對大起來的時候,比如需要網絡調用的時候,我們都沒有很好的辦法去維護它,不過我們同樣要掌握這種數據讀取和使用的方法。
針對於Excel還有個要考慮的問題,那就是版本的問題,因為excel分為97-03,07+版,也就是文件后綴是.xls和.xlsx的兩種版本(官網為什么不把14年前的版本干掉……要統一啊~),
所以,我們還要針對這兩種情況來整合封裝一個類,來實現根據后綴名判斷調用方法。
可以實現該功能的包有兩個:poi和jxl,至於優勢和劣勢自己網上搜吧。
1.這里先介紹下POI的一些用法,首先它更適用於97-2008版本的Excel(Apache-POI官網);
可能會用到的字段說明:
- HSSF - 提供讀寫Microsoft Excel XLS格式檔案的功能。
- XSSF - 提供讀寫Microsoft Excel OOXML XLSX格式檔案的功能。
- HWPF - 提供讀寫Microsoft Word DOC格式檔案的功能。
- HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。
- HDGF - 提供讀Microsoft Visio格式檔案的功能。
- HPBF - 提供讀Microsoft Publisher格式檔案的功能。
- HSMF - 提供讀Microsoft Outlook格式檔案的功能。
關於Maven引用:
<!-- poi-ooxml同時支持XLS和XLSX兩種格式 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency>
思路:
創建讀取Excel-xlsx格式的類;

/** * 讀取2007-2013格式 * @param filePath 文件路徑 * @return excel以數組形式返回 * @throws java.io.IOException */ @SuppressWarnings("rawtypes") public static List<Map> readXLSX(String filePath) throws IOException { List<Map> valueList = new ArrayList<Map>(); FileInputStream fis = null; try { fis = new FileInputStream(filePath); XSSFWorkbook xwb = new XSSFWorkbook(fis); // 構造 XSSFWorkbook 對象,strPath 傳入文件路徑 XSSFSheet sheet = xwb.getSheetAt(0); // 讀取第一章表格內容 // 定義 row、cell XSSFRow row; // 循環輸出表格中的第一行內容 表頭 Map<Integer, String> keys = new HashMap<Integer, String>(); row = sheet.getRow(0); if (row != null) { //System.out.println("j = row.getFirstCellNum()::"+row.getFirstCellNum()); //System.out.println("row.getPhysicalNumberOfCells()::"+row.getPhysicalNumberOfCells()); for (int j = row.getFirstCellNum(); j <= row.getPhysicalNumberOfCells(); j++) { // 通過 row.getCell(j).toString() 獲取單元格內容, if (row.getCell(j) != null) { if (!row.getCell(j).toString().isEmpty()) { keys.put(j, row.getCell(j).toString()); } } else { keys.put(j, "K-R1C" + j + "E"); } } } // 循環輸出表格中的從第二行開始內容 for (int i = sheet.getFirstRowNum() + 1; i <= sheet.getPhysicalNumberOfRows(); i++) { row = sheet.getRow(i); if (row != null) { boolean isValidRow = false; Map<String, Object> val = new HashMap<String, Object>(); for (int j = row.getFirstCellNum(); j <= row.getPhysicalNumberOfCells(); j++) { XSSFCell cell = row.getCell(j); if (cell != null) { String cellValue = null; if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) { if (DateUtil.isCellDateFormatted(cell)) { cellValue = new DataFormatter().formatRawCellContents(cell.getNumericCellValue(), 0, "yyyy-MM-dd HH:mm:ss"); } else { cellValue = String.valueOf(cell.getNumericCellValue()); } } else { cellValue = cell.toString(); } if (cellValue != null && cellValue.trim().length() <= 0) { cellValue = null; } val.put(keys.get(j), cellValue); if (!isValidRow && cellValue != null && cellValue.trim().length() > 0) { isValidRow = true; } } } // 第I行所有的列數據讀取完畢,放入valuelist if (isValidRow) { valueList.add(val); } } } } catch (IOException e) { e.printStackTrace(); } finally { fis.close(); } return valueList; }
創建讀取Excel-xls格式的類;

/** * 讀取97-2003格式 * @param filePath 文件路徑 * @throws java.io.IOException */ @SuppressWarnings("rawtypes") public static List<Map> readXLS(String filePath) throws IOException{ //返回結果集 List<Map> valueList=new ArrayList<Map>(); FileInputStream fis=null; try { fis=new FileInputStream(filePath); HSSFWorkbook wookbook = new HSSFWorkbook(fis); // 創建對Excel工作簿文件的引用 HSSFSheet sheet = wookbook.getSheetAt(0); // 在Excel文檔中,第一張工作表的缺省索引是0 int rows = sheet.getPhysicalNumberOfRows(); // 獲取到Excel文件中的所有行數 Map<Integer,String> keys=new HashMap<Integer, String>(); int cells=0; // 遍歷行(第1行 表頭) 作為Map里的key HSSFRow firstRow = sheet.getRow(0); if (firstRow != null) { // 獲取到Excel文件中的所有的列 cells = firstRow.getPhysicalNumberOfCells(); // 遍歷列 for (int j = 0; j < cells; j++) { // 獲取到列的值 try { HSSFCell cell = firstRow.getCell(j); String cellValue = getCellValue(cell); keys.put(j,cellValue); } catch (Exception e) { e.printStackTrace(); } } } // 遍歷行(從第二行開始) for (int i = 1; i < rows; i++) { // 讀取左上端單元格(從第二行開始) HSSFRow row = sheet.getRow(i); // 行不為空 if (row != null) { //准備當前行 所儲存值的map Map<String, Object> val=new HashMap<String, Object>(); boolean isValidRow = false; // 遍歷列 for (int j = 0; j < cells; j++) { // 獲取到列的值 try { HSSFCell cell = row.getCell(j); String cellValue = getCellValue(cell); val.put(keys.get(j),cellValue); if(!isValidRow && cellValue!=null && cellValue.trim().length()>0){ isValidRow = true; } } catch (Exception e) { e.printStackTrace(); } } //第I行所有的列數據讀取完畢,放入valuelist if(isValidRow){ valueList.add(val); } } } } catch (IOException e) { e.printStackTrace(); }finally { fis.close(); } return valueList; } /** * 返回表內正確的內字符類型 * @param cell 表格 * @return */ private static String getCellValue(HSSFCell cell) { DecimalFormat df = new DecimalFormat("#"); String cellValue=null; if (cell == null) return null; switch (cell.getCellType()) { case HSSFCell.CELL_TYPE_NUMERIC: if(HSSFDateUtil.isCellDateFormatted(cell)){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); cellValue=sdf.format(HSSFDateUtil.getJavaDate(cell.getNumericCellValue())); break; } cellValue=df.format(cell.getNumericCellValue()); break; case HSSFCell.CELL_TYPE_STRING: cellValue=String.valueOf(cell.getStringCellValue()); break; case HSSFCell.CELL_TYPE_FORMULA: cellValue=String.valueOf(cell.getCellFormula()); break; case HSSFCell.CELL_TYPE_BLANK: cellValue=null; break; case HSSFCell.CELL_TYPE_BOOLEAN: cellValue=String.valueOf(cell.getBooleanCellValue()); break; case HSSFCell.CELL_TYPE_ERROR: cellValue=String.valueOf(cell.getErrorCellValue()); break; } if(cellValue!=null&&cellValue.trim().length()<=0){ cellValue=null; } return cellValue; }
根據文件名選擇執行對應的類;

/** * 根據文件名自動識別讀取方式 * 同時支持xls及xlsx格式的Excel數據讀取 * * @param filepath 文件名:包含路徑及擴展名 * @return 返回列表內容格式: * 每一行數據都是以對應列的表頭為key 內容為value 比如 excel表格為: * =============== * A | B | C | D * ===|===|===|=== * 1 | 2 | 3 | 4 * ---|---|---|--- * a | b | c | d * --------------- * 返回 [{A=1, B=2, C=3, D=4}, {A=a, B=b, C=c, D=d}] * @throws java.io.IOException */ public static List<Map> readExcel(String filepath) { List<Map> valueList = new ArrayList<Map>(); try { if (new FileUtils().getuffix(filepath).equalsIgnoreCase("xlsx")) { valueList = ExcelUtils.readXLSX(filepath); } if (new FileUtils().getuffix(filepath).equalsIgnoreCase("xls")) { valueList = ExcelUtils.readXLS(filepath); } else { throw new ErrorOnSettingException("此文件不是Excel文件"); } } catch (Exception e) { e.printStackTrace(); } return valueList; }
文件名工具類;

public class FileUtils { /** * 獲取文件名 * * @param fileName 文件名包含擴展名 * @return 返回獲取的文件名 */ public String getFileName(String fileName) { fileName = fileName.trim(); String fName = fileName.substring(fileName.lastIndexOf("/") + 1); return fName; } }
實現類:
ExcelUtils.readExcel(filepath);
JDBC數據的讀取
本文最后要介紹JDBC的數據讀取,如何使用數據庫的數據到你的腳本中使用,也是數據參數驅動的一種常用方式;
針對數據量相對大的場景,首推數據庫來保存你的參數,也更方便維護;
相對於其他方式,jdbc在使用結束后,一定要關閉輸入流,就像webdriver使用結束后要有個webdriver.close()是一樣的,下邊的代碼中你也可以看到。
假如數據庫中的數據如下:
數據庫配置與鏈接:

// 數據庫類型 private static String DBTYPE = "mysql"; // 數據庫地址 格式:jdbc:mysql://ip:port/tablename?參數(可有可無,根據需要添加參數) private static String DB_URL = "jdbc:mysql://localhost:3306/antmember_test?characterEncoding=utf8&useSSL=true&serverTimezone=UTC"; // 數據庫登錄賬號 private static String USERNAME = ""; // 數據庫登錄密碼 private static String PASSWORD = ""; // 數據庫表名 private static String TABLENAME = ""; // 鏈接數據庫 private static Connection conn = null; // 向數據庫發送sql語句 private static Statement statement = null; // 返回sql執行結果 private static ResultSet resultSet = null; /** * 根據config配置信息,鏈接對應的數據庫 */ public static void dbConnect () { System.out.println("讀取數據庫配置並開始鏈接"); try { if (DBTYPE.equals("mysql")) { Class.forName("com.mysql.cj.jdbc.Driver"); } if (DBTYPE.equals("oracle")) { Class.forName("oracle.jdbc.driver.OracleDriver"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } }
數據庫的查詢:

public static void dbSelect (String sql, String detail){ dbConnect(); try { //登錄數據庫 conn = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD); //創建並執行sql語句 statement = conn.prepareStatement(sql); //返回sql執行結果 resultSet = statement.executeQuery(sql); JsonObject object = new JsonObject(); JsonArray array = new JsonArray(); while (resultSet.next()) { JsonObject ob = new JsonObject(); ob.addProperty(detail, resultSet.getString(detail)); array.add(ob); } object.add("查詢結果:", array); System.out.println(object.toString()); } catch (SQLException e) { e.printStackTrace(); } finally { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
為了方便查看返回結果,我還用了json解析格式輸出到控制台中,同樣,你也可以輸出到你的文檔中,或者日志當中(json解析需要引入gson這個jar包,maven的方法我就不說了),另外,關於其他數據庫的鏈接方式如下:
# 連接MySql數據庫:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password"); # 連接Oracle數據庫:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password"); # 連接SqlServer數據庫:Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password"); # 其他參數如:useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
使用:
//我的表名:sm_user_info
String sql = "SELECT * FROM sm_user_info WHERE id_type = 'git'";
dbSelect(sql, "user_id");
控制台上的執行結果:
至此,本文介紹完畢。
當然還有很多可以講的東西,大家不妨如此這般的各種嘗試一下吧。
有問題可以留言下方,我會及時解答,並補充說明