07.05 軟件測試自動化測試報告:依賴Jacob自動生成

軟件測試自動化測試報告:依賴Jacob自動生成

引言

項目上線前,一般都要求測試經理提供測試報告。雖說每個項目實現的需求不盡一樣,但測試報告的模板往往是一致的,且大多是word格式的。接下來介紹一個word操作神器—Jacob,用於自動生成測試報告。

Jacob介紹

JACOB is a JAVA-COM Bridge that allows you to call COM Automation components from Java. It uses JNI to make native calls to the COM libraries. JACOB runs on x86 and x64 environments supporting 32 bit and 64 bit JVMs。

官網下載地址

  • jdk如果是32位,則需將jacob-1.18-x86.dll放到C:\\Windows\\System32目錄下,64位則將jacob-1.18-x64.dll 放到C:\\Windows\\SysWOW64目錄下。
  • ps:java -version可查看jdk位數。
  • 將jacob-XXX.dll放到 jdk安裝目錄的jre\\bin目錄下。

實現步驟

1、 定義測試報告模板

2、分析案例執行結果文件,填充測試報告模板

軟件測試自動化測試報告:依賴Jacob自動生成

案例執行結果文件(ps:該文件由工具生成)

根據案例執行結果文件分析缺陷信息及案例執行狀態。

public static Map<string> countBug(String caseBugFilePath) throws BiffException,
IOException{
Map<string> testModelMap = new HashMap<string>();

InputStream instream = new FileInputStream(caseBugFilePath);
Workbook rwb = Workbook.getWorkbook(instream);

Sheet sheet = rwb.getSheet(0);

int rsRows = sheet.getRows(); // 獲取總行數,案例數為總行數-1
int caseNum = rsRows -1;

int caseExcute = 0; //已執行的用例(出去執行結果是“未執行”的用例)

String testModel =""; if(!sheet.getCell(1, 1).getContents().equals(""))
testModel = sheet.getCell(1, 1).getContents();
//第2列第2行,初始化功能點描述
else
JOptionPane.showMessageDialog(null, "【案例執行結果文件有誤,請檢查】",
"message",JOptionPane.ERROR_MESSAGE);
int caseNumTemp = 0;
StringBuffer buffer = new StringBuffer();
for (int i = 1; i < rsRows; i++) { //第一行為表頭
String excuteResult = sheet.getCell(8, i).getContents();
// 第9列第i行,執行結果
if(!excuteResult.trim().equals("未執行"))
caseExcute++; //統計已執行的用例總數
if(sheet.getCell(1, i).getContents().equals(testModel)){
caseNumTemp++; //模塊對應的案例數

String bugMess = sheet.getCell(10, i).getContents();
// 第11列第i行,缺陷信息
if(!bugMess.equals(""))
buffer.append(bugMess); //提取缺陷信息

testModelMap.put(testModel, caseNumTemp+":"+buffer.toString());
//key為 模塊名,value為 案例數,缺陷信息
}else{
caseNumTemp = 1;
buffer.delete(0, buffer.length()); //清空buffer信息

String bugMess = sheet.getCell(10, i).getContents();
// 第11列第i行,缺陷信息
if(!bugMess.equals(""))
buffer.append(bugMess); //提取缺陷信息

testModel = sheet.getCell(1, i).getContents().trim();
//重新賦值給testModel

testModelMap.put(testModel, caseNumTemp+":"+buffer.toString());
//key為 模塊名,value為 案例數,缺陷信息
}
}

testModelMap.put("${caseNum}", caseNum +""); //用例總數
testModelMap.put("${caseExcute}", caseExcute+""); //已執行的用例

System.out.println("用例總數:" + caseNum);
System.out.println("已執行的用例數:" + caseExcute);
System.out.println("模塊信息:\\n" + testModelMap);
return testModelMap;
}
/<string>/<string>/<string>

替換測試報告模板定義的變量、插入附件、生成目錄。

public class TestReport {
public static String report(String systemName,String author,String projectName,
String caseBugFilePath) throws IOException, BiffException{

Map<string> map = new HashMap<string>();
/*
* map規則為:key=模塊,value=案例數:缺陷信息
* {模塊1=49:GYL-1859_[關閉] GYL-1865_[關閉] GYL-1861_[關閉] GYL-1866_[關閉] ,
模塊2=188:}
*/
map = BugCount.countBug(caseBugFilePath); //統計缺陷及案例信息

JacobFunction m = new JacobFunction(false);
m.createNewDocument();
try{
File file1 = new File("template\\\\測試報告模板.doc");
File file1_temp = new File("template\\\\測試報告模板temp.doc");
m.copyFile(file1,file1_temp);
//拷貝文件,防止生成測試報告失敗,測試報告模板.doc被改寫

File file2 = new File("testReport\\\\"+systemName+"_"+projectName+"_測試報告"+".doc");

m.openDocument(file1_temp.getAbsolutePath());
//m.moveStart();

Date dt=new Date();
SimpleDateFormat matter1=new SimpleDateFormat("yyyy-MM-dd");
m.replaceAllText("${date}",matter1.format(dt)); //當前日期

m.replaceAllText("${author}",author); //作者
m.replaceAllText("${project}",projectName); //項目名稱

System.out.println("用例總數:"+ map.get("${caseNum}"));
m.replaceAllText("${caseNum}",map.get("${caseNum}")); //用例總數

System.out.println("已執行的用例:"+ map.get("${caseExcute}"));
m.replaceAllText("${caseExcute}",map.get("${caseExcute}")); //已執行的用例

int modelNum = map.size()-2; //除去 用例總數和已執行額用例,剩下為功能模塊數
m.replaceAllText("${modelNum}",modelNum+""); //功能模塊數

int i = 1;
int caseNumPerModel = 0; //每個功能模塊對應的案例數
int bugClosePerModel = 0; //每個功能模塊 關閉的缺陷數
int bugHupPerModel = 0; //每個功能模塊 掛起的缺陷數
int bugOtherPerModel = 0; //每個功能模塊 其他狀態的缺陷數
int bugCloseTotal = 0; //關閉缺陷數 總數
int bugHupTotal = 0; //掛起缺陷數 總數
int bugOtherTotal = 0; //其他狀態缺陷數 總數

for(String key:map.keySet()){
if(!key.equals("${caseNum}") && !key.equals("${caseExcute}")){
m.addLastTableRow(6); //表格增加一行
m.putTxtToCell(6,1+i,1,key); //填充功能模塊名稱
m.putTxtToCell(6,1+i,4,"100%"); //填充覆蓋率

String[] array = map.get(key).split(":");
caseNumPerModel = Integer.parseInt(array[0]);
m.putTxtToCell(6,1+i,2,caseNumPerModel+""); //填充規則點(模塊案例數)
m.putTxtToCell(6,1+i,3,caseNumPerModel+""); //填充已覆蓋(模塊案例數)

m.addLastTableRow(7); //表格增加一行
m.putTxtToCell(7,1+i,1,key); //填充功能模塊名稱

if(array.length == 1){ //表示沒有缺陷
m.putTxtToCell(7,1+i,2,"0"); //填充每個功能模塊 關閉的缺陷數
m.putTxtToCell(7,1+i,3,"0"); //填充每個功能模塊 掛起的缺陷數
m.putTxtToCell(7,1+i,4,"0"); //填充每個功能模塊 其他狀態的缺陷數

}else{ String array1[] = array[1].toString().trim().split(" ");
for(int j =0;j<array1.length> if(array1[j].indexOf("關閉") >= 0 ){
bugClosePerModel++; //每個模塊關閉的缺陷數

bugCloseTotal++;
}
else
if(array1[j].indexOf("缺陷掛起") >= 0){
bugHupPerModel++; //每個模塊 缺陷掛起數
bugHupTotal++;
} else{
bugOtherPerModel++; //每個模塊 其他的狀態缺陷數
bugOtherTotal++;
}
}
m.putTxtToCell(7,1+i,2,bugClosePerModel+""); //填充每個功能模塊 關閉的缺陷數
m.putTxtToCell(7,1+i,3,bugHupPerModel+""); //填充每個功能模塊 掛起的缺陷數
m.putTxtToCell(7,1+i,4,bugOtherPerModel+""); //填充每個功能模塊 其他狀態的缺陷數
}

i++;
bugClosePerModel =0; //下次循環前再初始化
bugHupPerModel =0; //下次循環前再初始化
bugOtherPerModel =0; //下次循環前再初始化
}
}

m.replaceAllText("${bugCloseTotal}",bugCloseTotal+""); //關閉的缺陷總數
m.replaceAllText("${bugHupTotal}",bugHupTotal+""); //掛起的缺陷總數
m.replaceAllText("${bugOtherTotal}",bugOtherTotal+""); //其他狀態的缺陷總數
int bugNum = bugCloseTotal + bugHupTotal + bugOtherTotal;
m.replaceAllText("${bugNum}",bugNum+""); //缺陷總數

System.out.println("功能模塊數:"+modelNum);
System.out.println("關閉的缺陷總數:"+ bugCloseTotal);
System.out.println("掛起的缺陷總數:"+ bugHupTotal);
System.out.println("其他狀態的缺陷總數:"+ bugOtherTotal);
System.out.println("缺陷總數:"+ bugNum);



File file = new File(caseBugFilePath);
String caseBugFileName = file.getName();

m.replaceFile("${insertCaseBugFile}", caseBugFilePath, caseBugFileName); //插入附件

//m.addLastTableRow(6);
//m.putTxtToCell(6,1,1,"tomandytomandyddddd");

m.createContents("${contents}"); //生成目錄

//String[] a = {"s","dd"};
//int b = Integer.parseInt(a[0]) ; //模擬異常

m.save(file2.getAbsolutePath()); //保存測試報告
m.close();
return "success";

}catch (Exception e){
m.close();
//如果生成測試報告異常,則關閉文檔,防止報錯“測試報告模板temp.doc已被佔用”
System.out.println(e.getMessage()); return e.getMessage();
}

}
}
/<array1.length>/<string>/<string>

Jacob操作word的各類方法。

public class JacobFunction { 
// word文檔 private Dispatch doc;
// word運行程序對象
private static ActiveXComponent word;
// 所有word文檔集合 private Dispatch documents;
// 選定的範圍或插入點
private static Dispatch selection;
private boolean saveOnExit = true;

/** *//**
*

* @param visible 為true表示word應用程序可見
*/ public JacobFunction(boolean visible) { //是否打開word應用程序
if (word == null) {
word = new ActiveXComponent("Word.Application");
word.setProperty("Visible", new Variant(visible));
} if (documents == null)
documents = word.getProperty("Documents").toDispatch();
}

/** *//**
* 從選定內容或插入點開始查找文本
*
* @param toFindText 要查找的文本
* @return boolean true-查找到並選中該文本,false-未查找到文本
*/ public static boolean find(String toFindText) {
if (toFindText == null || toFindText.equals(""))

return false;
// 從selection所在位置開始查詢
Dispatch find = word.call(selection, "Find").toDispatch();
// 設置要查找的內容 Dispatch.put(find, "Text", toFindText);
// 向前查找 Dispatch.put(find, "Forward", "True");
// 設置格式 Dispatch.put(find, "Format", "True");
// 大小寫匹配 Dispatch.put(find, "MatchCase", "True");
// 全字匹配 Dispatch.put(find, "MatchWholeWord", "True");
// 查找並選中 //System.out.println("查找");
return Dispatch.call(find, "Execute").getBoolean();
}
/** *//**
* 全局替換文本
*
* @param toFindText 查找字符串
* @param newText 要替換的內容
*/ public void replaceAllText(String toFindText, String newText) {
moveStart(); //移到文件開頭
while (find(toFindText)) {
Dispatch.put(selection, "Text", newText);
Dispatch.call(selection, "MoveRight");
moveRight(1); //System.out.println("替換"); }

}

public void moveRight( int pos) { if (selection == null )
selection = Dispatch.get(word, "Selection" ).toDispatch();
for ( int i = 0 ; i < pos; i++)
Dispatch.call(selection, "MoveRight" );
}
/** *//**
* 創建一個新的word文檔
*
*/ public void createNewDocument() {
doc = Dispatch.call(documents, "Add").toDispatch();
selection = Dispatch.get(word, "Selection").toDispatch();
}

/** *//**
* 打開一個已存在的文檔
*
* @param docPath
*/ public void openDocument(String docPath) {
closeDocument();
doc = Dispatch.call(documents, "Open", docPath).toDispatch();
selection = Dispatch.get(word, "Selection").toDispatch();
}

/** *//**
* 文件保存或另存為
*
* @param savePath 保存或另存為路徑
* @throws IOException
*/ public void save(String savePath) throws IOException {

File file = new File(savePath);
file.createNewFile();
Dispatch.call(
(Dispatch) Dispatch.call(word, "WordBasic").getDispatch(), "FileSaveAs", savePath);
}

/** *//**
* 關閉當前word文檔
*
*/ public void closeDocument() { if (doc != null) {
//Dispatch.call(doc, "Save");
Dispatch.call(doc, "Close", new Variant(saveOnExit));
doc = null;
}

}

/** *//**
* 關閉全部應用
*
*/ public void close() {
closeDocument(); if (word != null) {
Dispatch.call(word, "Quit");
word = null;
}
selection = null;
documents = null;
}

/** *//**
* 把插入點移動到文件首位置
*
*/ public void moveStart() { if (selection == null)
selection = Dispatch.get(word, "Selection").toDispatch();
Dispatch.call(selection, "HomeKey", new Variant(6));
}

/** *//**
*
* @param toFindText 要查找的字符串
* @param imagePath 文件路徑
* @return */public boolean replaceFile
(String toFindText, String insertFilePath,String fileName) {
moveStart(); //從文件首位置開始 if (!find(toFindText)) return false;
System.out.println("文件路徑:"+insertFilePath);
System.out.println("文件名:"+fileName);
Dispatch.call(word, "Run", new Variant("InsertCaseMess"),
new Variant(insertFilePath),new Variant(fileName));
return true;
}
/** *//**
* 生成目錄
* @param toFindText 要查找的字符串
* @return */
public boolean createContents(String toFindText){
moveStart(); //從文件首位置開始
if (!find(toFindText)) return false;

Dispatch alignment = Dispatch.get(selection, "ParagraphFormat")

.toDispatch(); // 行列格式化需要的對象
Dispatch.put(alignment, "Alignment", "1");
// (1:置中 2:靠右 3:靠左) // insertNewParagraph();
//moveRight(1);
Dispatch range = Dispatch.get(this.selection, "RANGE").toDispatch();
Dispatch fields = Dispatch.call(this.selection, "FIELDS").toDispatch();
Variant call = Dispatch.call(fields, "ADD",
range, new Variant(-1),
new Variant("TOC"), new Variant(true));
Dispatch tablesOfContents = Dispatch.call
(doc, "TablesOfContents").toDispatch();// 整個目錄區域
/ 整個目錄 Dispatch tableOfContents = Dispatch.call(tablesOfContents,
"Item", new Variant(1)).toDispatch();
// 拿到整個目錄的範圍
Dispatch tableOfContentsRange = Dispatch.get(tableOfContents, "Range").
toDispatch();//
// 取消選中,應該就是移動光標
Dispatch format = Dispatch.get(tableOfContentsRange,
"ParagraphFormat").toDispatch();//
// 設置段落格式為首行縮進2個字符
Dispatch.put(format, "CharacterUnitLeftIndent", new Variant(1));
return true;
}
/** *//**
* 在最後1行前增加一行
*
* @param tableIndex
* word文檔中的第N張表(從1開始)
*/ public void addLastTableRow(int tableIndex) { // 所有表格 Dispatch tables = Dispatch.get(doc, "Tables").toDispatch(); // 要填充的表格 Dispatch table = Dispatch.call(tables, "Item", new Variant(tableIndex))
.toDispatch(); // 表格的所有行 Dispatch rows = Dispatch.get(table, "Rows").toDispatch();
Dispatch row = Dispatch.get(rows, "Last").toDispatch();
Dispatch.call(rows, "Add", new Variant(row));
}

/** *//**
* 在指定的單元格里填寫數據
*
* @param tableIndex
* @param cellRowIdx
* @param cellColIdx

* @param txt
*/ public void putTxtToCell(int tableIndex, int cellRowIdx, int cellColIdx,
String txt) { // 所有表格
Dispatch tables = Dispatch.get(doc, "Tables").toDispatch();
// 要填充的表格
Dispatch table = Dispatch.call(tables, "Item", new Variant(tableIndex))
.toDispatch();
Dispatch cell = Dispatch.call(table, "Cell", new Variant(cellRowIdx), new Variant(cellColIdx)).toDispatch();
Dispatch.call(cell, "Select");
Dispatch.put(selection, "Text", txt);
}

public void copyFile(File fromFile,File toFile) throws IOException{
FileInputStream ins = new FileInputStream(fromFile);
FileOutputStream out = new FileOutputStream(toFile);
byte[] b = new byte[1024]; int n=0; while((n=ins.read(b))!=-1){
out.write(b, 0, n);
}

ins.close();
out.close();
}
}

3、插入附件。

第2點的代碼實現了插入附件的功能,但有以下幾點需要關注。以wps為例,錄製宏,然後再通過java調用宏來實現插入附件。

首先,使用wps錄製一個插入附件的宏,命名為“InsertCaseMess”,步驟如下。

軟件測試自動化測試報告:依賴Jacob自動生成

錄製宏

軟件測試自動化測試報告:依賴Jacob自動生成

點擊“確定”按鈕後,再進行“插入附件”操作。

軟件測試自動化測試報告:依賴Jacob自動生成

隨便插入一個附件,然後“停止錄製”。

軟件測試自動化測試報告:依賴Jacob自動生成

點擊“VB編輯器”,修改“InsertCaseMess腳本”如下並保存。

Sub InsertCaseMess(filePath As String, fileName As String)'
' InsertCaseMess Macro' 宏由 lenovo 錄製,時間: 2018/05/21
''
Selection.InlineShapes.AddOLEObject fileName:=filePath,
LinkToFile:=0, DisplayAsIcon:=-1, IconFileName:=
"C:\\Users\\lenovo\\AppData\\Local\\Kingsoft\\WPS Office\\10.1.0.7245\\office6\\et.exe",
IconIndex:=3, IconLabel:=fileName
End Sub

其次,在測試報告模板中定義“${insertCaseBugFile}”變量,通過replaceFile方法把變量替換為附件。

軟件測試自動化測試報告:依賴Jacob自動生成

測試報告模板

m.replaceFile("${insertCaseBugFile}", caseBugFilePath, caseBugFileName); 
//插入附件
/** *//**
*
* @param toFindText 要查找的字符串
* @param imagePath 文件路徑
* @return*/ public boolean replaceFile(String toFindText,

String insertFilePath,String fileName) {
moveStart(); //從文件首位置開始if (!find(toFindText)) return false;
System.out.println("文件路徑:"+insertFilePath);
System.out.println("文件名:"+fileName);
Dispatch.call(word, "Run", new Variant("InsertCaseMess"),
new Variant(insertFilePath),new Variant(fileName));return true;
}

測試報告

軟件測試自動化測試報告:依賴Jacob自動生成


知乎:請搜索(馬蟻蛋)

公眾號:軟件測試資源站(ID:testpu)

關注後私信回覆 入群,加入自學社群聯盟


分享到:


相關文章: