├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── github
│ │ └── qihootest
│ │ └── leo
│ │ ├── dispatch
│ │ ├── DispatchConf.java
│ │ ├── ExecTask.java
│ │ ├── log
│ │ │ └── TestngLog.java
│ │ ├── report
│ │ │ ├── ExportReportHtml.java
│ │ │ ├── TestReport.java
│ │ │ └── TngCount.java
│ │ ├── run
│ │ │ ├── TestRunInfo.java
│ │ │ ├── TestngRun.java
│ │ │ └── TestngRunSingle.java
│ │ └── testcase
│ │ │ ├── CreateXmlFile.java
│ │ │ ├── DataFileCase.java
│ │ │ ├── ICase.java
│ │ │ ├── JavaFileCase.java
│ │ │ ├── SuperCase.java
│ │ │ └── XmlFileCase.java
│ │ ├── ift
│ │ ├── IftConf.java
│ │ ├── IftExec.java
│ │ ├── core
│ │ │ ├── CasesUtils.java
│ │ │ ├── CompareResult.java
│ │ │ └── IFtResultInfo.java
│ │ ├── testcase
│ │ │ ├── IftTestCase.java
│ │ │ ├── autocreate
│ │ │ │ ├── CreateJavaFile.java
│ │ │ │ ├── IftDataFileCase.java
│ │ │ │ └── JavaCaseInfo.java
│ │ │ └── format
│ │ │ │ ├── FormatCase.java
│ │ │ │ └── ReadCaseFromExcel.java
│ │ └── util
│ │ │ ├── CommonSign.java
│ │ │ └── ExportReportExcel.java
│ │ └── toolkit
│ │ ├── QtafException.java
│ │ ├── TkConf.java
│ │ ├── dbunit
│ │ ├── DbUnitUtil.java
│ │ └── data
│ │ │ └── excel
│ │ │ ├── XlsDataSet.java
│ │ │ ├── XlsDataSetWriter.java
│ │ │ ├── XlsTable.java
│ │ │ └── XlsTableWrapper.java
│ │ ├── httpclient
│ │ ├── HttpUtil.java
│ │ ├── HttpsUtil.java
│ │ └── ResponseInfo.java
│ │ ├── mysql
│ │ ├── ConnMysql.java
│ │ ├── MysqlUtil.java
│ │ └── dao
│ │ │ ├── DaoUtil.java
│ │ │ ├── ITable.java
│ │ │ └── Table.java
│ │ └── util
│ │ ├── CommUtils.java
│ │ ├── CompilerUtil.java
│ │ ├── ExcelUtil.java
│ │ ├── FileUtil.java
│ │ ├── FreeMakerUtil.java
│ │ ├── JsonUtil.java
│ │ ├── LogUtil.java
│ │ ├── PropUtil.java
│ │ ├── SftpExecUtil.java
│ │ ├── SftpFileUtil.java
│ │ ├── StringUtil.java
│ │ └── XmlUtil.java
└── resources
│ ├── IftConf.properties
│ ├── Template.ftl
│ ├── log4j.properties
│ └── testng-results.xsl
└── test
└── java
└── com
└── github
└── qihootest
└── leo
├── dispatch
├── TestDispatchConf.java
├── TestExecTask.java
└── TestTestngLog.java
├── ift
└── core
│ └── CompareResultTest.java
└── toolkit
├── TestDbunitUtil.java
├── TestMysqlUtil.java
└── att_click_info.java
/README.md:
--------------------------------------------------------------------------------
1 | Leo
2 | =====
3 |
4 | 接口自动化测试框架 1.0.7-SNAPSHOT
5 |
6 | 1.0.7-SNAPSHOT 更新内容
7 | -----------------------------------------------------
8 | 1.增加对cookie的接口依赖;在预期结果中增加${cookie},会将cookie做为变量传递到其它参数中;
9 | 2.增加httpClient线程池管理,解决多线程请求时“[ERROR] CasesUtils - Invalid use of BasicClientConnManager: connection still allocated.”的问题;
10 | 3.修复多线程时更新请求参数时资源同步问题;(现在业务类方法不需要继承casesUtils)
11 |
12 |
13 |
14 | 1.0.6 更新内容
15 | -------------------------------
16 | 1. 增加接口依赖,以${}标示,预期结果中支持多个相同参数以下划线加后缀进行区分,eg:${key_01};
17 | 2. 请求参数中支持依赖参数部分替换;eg:value${key_01}value 只会替换${}中的内容;
18 | 3. 修复当有多个数据库连接操作时,只能实例化一个数据库连接;
19 |
20 |
21 | 1.0.5 更新内容
22 | -------------------------------
23 | 1. 解决生成javadoc时WARNING;
24 | 2. 当预期结果为int类型时,对实际结果为0进行判断(0为int结果匹配中的特殊值,0不包含在关键词int中);
25 | 3. 支持用例多线程方式,提高运行效率;
26 | 4. 支持对Sheet中的多用例进行多线程(即:methods方式);可对多个Sheet页进行多线程执行(即:tests方式);
27 | 5. 可自定义线程数;
28 | 6. 支持实际结果为json数组的处理;相同的key,value用数组表示,预期结果可写成数组行完全匹配,也可以写单个值进行包含匹配;
29 |
30 |
31 |
32 | 1.0.4 更新内容:
33 | -------------------------------
34 | 1. 增加设置预期结果中"date"关键词dateFormat样式,可自定义关键词date样式;
35 | 2. 预期结果中增加对int关键词的支持,匹配结果中int类型动态值,支持单个和多个实际结果(key=int或key=int^int);
36 | 3. 增加Host关键字及全局配置;
37 | 4. 增加Header全局配置;
38 | 5. 修改注解;
39 |
40 |
41 | 1.0.3 更新内容:
42 | -------------------------------
43 | 1. pom.xml中增加邮件包依赖;
44 | 2. 增加https请求处理(支持证书认证与非证书认证两种方式)
45 | 3. 修改解析xml时一个key对应多个值时,实际结果值与预期结果值无法进行比较(key=[value1,value2])
46 | 4. CompareResult.trimActres 修改为public类型提供业务处理使用;
47 | 5. 修改代理默认值bug;
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | org.sonatype.oss
4 | oss-parent
5 | 7
6 |
7 |
8 |
9 |
10 |
11 | sonatype-nexus-snapshots
12 | Sonatype Nexus Snapshots
13 | https://oss.sonatype.org/content/repositories/snapshots
14 |
15 |
16 |
17 | sonatype-nexus-staging
18 | Nexus Staging Repository
19 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
20 |
21 |
22 |
23 | 4.0.0
24 | com.github.qihootest
25 | leo
26 | 1.0.7-SNAPSHOT
27 | jar
28 |
29 | leo
30 | https://github.com/QihooTest/Leo
31 |
32 |
33 |
34 | The Apache Software License, Version 2.0
35 | http://www.apache.org/licenses/LICENSE-2.0.txt
36 | repo
37 |
38 |
39 |
40 | scm:svn:https://github.com/QihooTest/Leo/trunk/
41 | scm:svn:https://github.com/QihooTest/Leo/trunk/
42 | https://github.com/QihooTest/Leo/trunk/
43 |
44 |
45 | UTF-8
46 | true
47 |
48 |
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-compiler-plugin
54 | 2.3.2
55 |
56 | 7
57 | 7
58 | UTF-8
59 | UTF-8
60 |
61 |
62 |
63 | org.apache.maven.plugins
64 | maven-surefire-plugin
65 | 2.17
66 |
67 | -Dfile.encoding=UTF-8
68 |
69 | **/*.java
70 |
71 |
72 | once
73 |
74 |
75 |
76 | maven-assembly-plugin
77 |
78 |
79 | jar-with-dependencies
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | src/main/resources
89 |
90 | **/*.*
91 |
92 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | org.testng
104 | testng
105 | 6.8.7
106 | jar
107 | compile
108 |
109 |
110 |
111 | net.sf.saxon
112 | saxon
113 | 8.7
114 | jar
115 | compile
116 |
117 |
118 |
119 | org.freemarker
120 | freemarker
121 | 2.3.20
122 | jar
123 | compile
124 |
125 |
126 | log4j
127 | log4j
128 | 1.2.17
129 | jar
130 | compile
131 |
132 |
133 | org.apache.poi
134 | poi
135 | 3.9
136 | jar
137 | compile
138 |
139 |
140 | org.apache.poi
141 | poi-ooxml
142 | 3.9
143 | jar
144 | compile
145 |
146 |
147 | org.apache.poi
148 | poi-ooxml-schemas
149 | 3.9
150 | jar
151 | compile
152 |
153 |
154 | commons-codec
155 | commons-codec
156 | 1.8
157 | jar
158 | compile
159 |
160 |
161 | commons-lang
162 | commons-lang
163 | 2.6
164 | jar
165 | compile
166 |
167 |
168 | commons-logging
169 | commons-logging
170 | 1.1.3
171 | jar
172 | compile
173 |
174 |
175 | org.apache.httpcomponents
176 | fluent-hc
177 | 4.2.5
178 | jar
179 | compile
180 |
181 |
182 | org.apache.httpcomponents
183 | httpclient
184 | 4.2.5
185 | jar
186 | compile
187 |
188 |
189 | org.apache.httpcomponents
190 | httpclient-cache
191 | 4.2.5
192 | jar
193 | compile
194 |
195 |
196 | org.apache.httpcomponents
197 | httpcore
198 | 4.2.5
199 | jar
200 | compile
201 |
202 |
203 | org.apache.httpcomponents
204 | httpmime
205 | 4.2.5
206 | jar
207 | compile
208 |
209 |
210 | commons-beanutils
211 | commons-beanutils
212 | 1.8.3
213 | jar
214 | compile
215 |
216 |
217 | commons-collections
218 | commons-collections
219 | 3.2.1
220 | jar
221 | compile
222 |
223 |
224 | net.sf.ezmorph
225 | ezmorph
226 | 1.0.6
227 | jar
228 | compile
229 |
230 |
231 | net.sf.json-lib
232 | json-lib
233 | 2.4
234 | jar
235 | jdk15
236 | compile
237 |
238 |
239 | com.jcraft
240 | jsch
241 | 0.1.50
242 | jar
243 | compile
244 |
245 |
246 | dom4j
247 | dom4j
248 | 1.6.1
249 | jar
250 | compile
251 |
252 |
253 |
254 | commons-io
255 | commons-io
256 | 2.4
257 | jar
258 | compile
259 |
260 |
261 |
262 | mysql
263 | mysql-connector-java
264 | 5.1.26
265 | jar
266 | compile
267 |
268 |
269 |
270 | org.dbunit
271 | dbunit
272 | 2.4.9
273 | jar
274 | compile
275 |
276 |
277 | org.slf4j
278 | slf4j-log4j12
279 | 1.7.5
280 | jar
281 | compile
282 |
283 |
284 | org.slf4j
285 | slf4j-api
286 | 1.7.5
287 | jar
288 | compile
289 |
290 |
291 |
292 | javax.mail
293 | mail
294 | 1.4.7
295 | jar
296 | compile
297 |
298 |
299 |
300 |
301 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/DispatchConf.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch;
2 | import java.io.File;
3 | import java.io.FileInputStream;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.InputStreamReader;
8 | import java.io.OutputStream;
9 | import java.util.Properties;
10 |
11 | import org.apache.commons.io.FileUtils;
12 |
13 |
14 | /**
15 | * 配置文件
16 | * @author lianghui (lianghui@360.cn)
17 | *
18 | */
19 | public class DispatchConf {
20 |
21 | /**
22 | * 类文件输出路径
23 | */
24 | public static String ClsPath = getClassPath();
25 | /**
26 | * 项目根目录
27 | */
28 | public static String RootPath = getRootPath();
29 | /**
30 | * suites默认存储目录
31 | */
32 | public static String SuitsXmlPath = RootPath+"leo/dispatch/suites/";//getPropValue("SuitsXmlPath", "tba/suites/");
33 | /**
34 | * testng默认输出目录
35 | */
36 | public static String TestNgOutPath = RootPath+"leo/dispatch/testng-out/";
37 | /**
38 | * html测试报告默认输出目录
39 | */
40 | public static String HtmlReportOutPath = RootPath+"leo/dispatch/report/"+System.currentTimeMillis()+"/";
41 | /**
42 | * html测试报告默认标题
43 | */
44 | public static String HtmlReportTitle = "测试报告";
45 | /**
46 | * testNGXslt插件配置文件路径
47 | */
48 | public static String TestNGXsltFile = RootPath+"leo/dispatch/testng-results.xsl";
49 |
50 | /**
51 | * 写配置文件,如果不存在则创建
52 | * @return boolean 已存在或创建失败时返回false 创建成功返回true
53 | */
54 | public static boolean writeConf() {
55 | if (!new File(DispatchConf.TestNGXsltFile).exists()) {
56 | return copyFile(DispatchConf.class.getResourceAsStream("/testng-results.xsl"),new File(TestNGXsltFile));
57 | }
58 | return false;
59 | }
60 |
61 | /**
62 | * 删除默认临时目录
63 | */
64 | public static void delTmpPath() {
65 | try {
66 | // 清空xml文件生成目录
67 | FileUtils.deleteDirectory(new File(SuitsXmlPath));
68 | // TestNG输出目录
69 | FileUtils.deleteDirectory(new File(TestNgOutPath));
70 | } catch (IOException e) {
71 | // e.printStackTrace();
72 | }
73 |
74 | }
75 |
76 | private static String getClassPath() {
77 | return DispatchConf.class.getClassLoader().getResource("").toString();
78 | }
79 | private static String getRootPath() {
80 | String path="";
81 | if (DispatchConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF")!=-1) {
82 | path=DispatchConf.class.getClassLoader().getResource("").toString().substring(6, DispatchConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF"));
83 | path=path+"/";
84 | }else {
85 | // path = GlobalSettings.class.getClassLoader().getResource("").toString();
86 | String temp=System.getProperty("user.dir");
87 | temp=temp.replace("\\","/");
88 | path=temp+"/";
89 | }
90 | return path;
91 | }
92 |
93 | @SuppressWarnings("unused")
94 | private static String getPropValue(String key, String defaultValue) {
95 | return getProperties().getProperty(key, defaultValue);
96 | }
97 |
98 | private static Properties getProperties() {
99 | Properties prop = new Properties();
100 | try {
101 | FileInputStream file = new FileInputStream("conf.properties");
102 | prop.load(new InputStreamReader(file,"UTF-8"));
103 | file.close();
104 | } catch (Exception e) {
105 | // e.printStackTrace();
106 | }
107 | return prop;
108 | }
109 |
110 | private static boolean copyFile(InputStream from, File to) {
111 | try {
112 | if (! to.getParentFile().exists()) {
113 | to.getParentFile().mkdirs();
114 | }
115 | OutputStream os = new FileOutputStream(to);
116 | byte[] buffer = new byte[65536];
117 | int count = from.read(buffer);
118 | while (count > 0) {
119 | os.write(buffer, 0, count);
120 | count = from.read(buffer);
121 | }
122 | os.close();
123 | return true;
124 | } catch (IOException e) {
125 | // e.printStackTrace();
126 | return false;
127 | }
128 |
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/ExecTask.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch;
2 |
3 | import com.github.qihootest.leo.dispatch.report.TestReport;
4 | import com.github.qihootest.leo.dispatch.run.TestRunInfo;
5 | import com.github.qihootest.leo.dispatch.run.TestngRunSingle;
6 | import com.github.qihootest.leo.toolkit.util.CommUtils;
7 | import com.github.qihootest.leo.toolkit.util.LogUtil;
8 |
9 |
10 | /**
11 | * 任务执行入口
12 | * @author lianghui (lianghui@360.cn)
13 | *
14 | */
15 | public class ExecTask {
16 |
17 | private LogUtil log=LogUtil.getLogger(ExecTask.class);//日志记录
18 | private TestngRunSingle task;
19 | private TestRunInfo runInfo;
20 | private TestReport report;
21 |
22 | /**
23 | * 构造函数
24 | */
25 | public ExecTask() {
26 | task = TestngRunSingle.getInstance();
27 | report = new TestReport();
28 | }
29 | /**
30 | * 任务执行
31 | * @return TestReport
32 | */
33 | public TestReport Exec() {
34 | //传入参数校验
35 | if (runInfo.getCaseList() == null || runInfo.getCaseList().size()<1) {
36 | setResNoAndMsg(-100,"待执行的用例不存在");
37 | log.error(runInfo.getTaskName()+" 待执行的用例不存在");
38 | return report;
39 | }
40 | Long startTimeMS=System.currentTimeMillis();
41 | Long startTime=startTimeMS/1000;
42 | int sumTime=0;//记录等待时长,单位秒
43 | while (true) {
44 | //获取等待的时间
45 | sumTime=(int) (System.currentTimeMillis()/1000-startTime);
46 | //判断当前是否有任务运行
47 | //当前无任务执行,执行当前任务
48 | if (!task.getFlag()) {
49 | //运行任务 设置任务信息
50 | task.setRunInfo(this.runInfo);
51 | report= task.execTask();
52 | long execTimes=System.currentTimeMillis()-startTimeMS;
53 | log.info(this.runInfo.getTaskName()+" 执行时间:"+execTimes+"毫秒");
54 | report.setSumTime(execTimes);
55 | return report;//执行完毕后返回测试报告信息
56 | }
57 |
58 | //当前有任务在运行 判断等待时间
59 | if (sumTime>600) {//设置等待超时时间600秒
60 | log.info(this.runInfo.getTaskName()+" 已等待"+sumTime+"秒,超时退出");
61 | setResNoAndMsg(-203,"任务等待超时,已等待"+sumTime+"秒");
62 | return report;
63 | }
64 | // 当前有任务在运行 等待10秒后再试试
65 | log.info(this.runInfo.getTaskName()+" 在等待执行,当前有任务在执行中。。。等待10秒后再尝试执行");
66 | CommUtils.sleep(10000);
67 | }//死循环
68 | }
69 |
70 | /**
71 | * 设置任务运行配置信息
72 | * @param runInfo
73 | */
74 | public void setRunInfo(TestRunInfo runInfo) {
75 | this.runInfo = runInfo;
76 | }
77 |
78 | private void setResNoAndMsg(int resNo,String resMsg) {
79 | this.report.setResNo(resNo);
80 | this.report.setResMsg(resMsg);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/log/TestngLog.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.log;
2 |
3 | import org.testng.Reporter;
4 | import com.github.qihootest.leo.toolkit.util.LogUtil;
5 |
6 |
7 |
8 | /**
9 | * 封装TestNG log记录
10 | * @author lianghui (lianghui@360.cn)
11 | *
12 | */
13 | public class TestngLog {
14 | private static LogUtil log=LogUtil.getLogger(TestngLog.class);//日志记录
15 | private static boolean OutputTestNGLog=true;//是否输出到TestNG Log日志
16 | /**
17 | * 说明:记录用例开始,写入TestNG日志
18 | *
19 | * @param testCaseName 用例名称
20 | */
21 | public static void CaseStart(String testCaseName) {
22 | TestNGLog("用例-------【" + testCaseName
23 | + "】----------开始执行---------------");
24 | }
25 |
26 | /**
27 | * 说明:记录用例结束,写入TestNG日志
28 | *
29 | * @param testCaseName 用例名称
30 | */
31 | public static void CaseEnd(String testCaseName) {
32 | TestNGLog("用例-------【" + testCaseName
33 | + "】----------执行完毕---------------");
34 | TestNGLog(
35 | "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////");
36 | }
37 |
38 | /**
39 | * 说明:把指定信息写入TestNG日志
40 | * @param str 字符串
41 | */
42 | public static void Log(String str) {
43 | TestNGLog(str);
44 | }
45 |
46 | /**
47 | * 设置是否输出TestNG执行记录到TestNG日志中
48 | * @param outputTestNGLog 默认true
49 | */
50 | public static void setOutputTestNGLog(boolean outputTestNGLog) {
51 | OutputTestNGLog = outputTestNGLog;
52 | }
53 |
54 | private static void TestNGLog(String info) {
55 | log.info(info);
56 | if (OutputTestNGLog) {
57 | Reporter.log(info,false);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/report/ExportReportHtml.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.report;
2 |
3 | import java.io.BufferedOutputStream;
4 | import java.io.FileOutputStream;
5 |
6 | import javax.xml.transform.Result;
7 | import javax.xml.transform.Source;
8 | import javax.xml.transform.Transformer;
9 | import javax.xml.transform.TransformerFactory;
10 | import javax.xml.transform.stream.StreamResult;
11 |
12 | import com.github.qihootest.leo.dispatch.DispatchConf;
13 | import com.github.qihootest.leo.toolkit.util.FileUtil;
14 | import com.github.qihootest.leo.toolkit.util.LogUtil;
15 |
16 |
17 | /**
18 | * TestNG的原生报告转换为美化后的Html格式测试报告
19 | * @author lianghui (lianghui@360.cn)
20 | *
21 | */
22 | public class ExportReportHtml {
23 |
24 | private static LogUtil log=LogUtil.getLogger(ExportReportHtml.class);//日志记录
25 |
26 | /**
27 | * 根据TestNG结果输出的xml文件,优化生成html格式测试报告
28 | * @param tngOutFilePath TestNG的结果xml文件路径
29 | * @param htmlReportPath html报告的目录
30 | * @param htmlReportTitle 测试报告标题
31 | * @return boolean 创建成功返回true
32 | */
33 | public static boolean createHtmlReport(String tngOutFilePath,String htmlReportPath,String htmlReportTitle) {
34 | if (!FileUtil.isExist(tngOutFilePath) ) {
35 | log.error("生成Html报告出错-testng输出的xml文件不存在:"+tngOutFilePath);
36 | return false;
37 | }
38 | if (!FileUtil.createDictory(htmlReportPath)){
39 | log.error("生成Html报告出错-输出目录创建失败:"+htmlReportPath);
40 | return false;
41 | }
42 | try {
43 | Source xml = new javax.xml.transform.stream.StreamSource(tngOutFilePath);
44 | Source xsl = new javax.xml.transform.stream.StreamSource(DispatchConf.TestNGXsltFile);
45 | Result out = new StreamResult(new BufferedOutputStream(new FileOutputStream(htmlReportPath+"/index.html")));
46 | // 创建转换器工厂
47 | TransformerFactory tfactory = TransformerFactory.newInstance();
48 | // 创建 XSL 转换器
49 | Transformer transformer = tfactory.newTransformer(xsl);
50 | //参数设置
51 | transformer.setParameter("testNgXslt.outputDir",htmlReportPath);
52 | //transformer.setParameter("testNgXslt.showRuntimeTotals", true);
53 | transformer.setParameter("testNgXslt.reportTitle", htmlReportTitle);
54 | transformer.transform(xml, out);
55 | log.info("生成Html测试报告成功:"+htmlReportPath+"/index.html");
56 | return true;
57 | } catch (Exception e) {
58 | log.error("生成Html报告出错-xml转换异常:"+e.getMessage());
59 | return false;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/report/TestReport.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.report;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * 测试任务执行完毕后输出的测试报告摘要信息
7 | *
8 | * @author lianghui (lianghui@360.cn)
9 | */
10 | public class TestReport {
11 |
12 | /**
13 | * 任务ID
14 | */
15 | private String taskId;
16 | /**
17 | * 任务名称
18 | */
19 | private String taskName;
20 | /**
21 | * html报告存储位置
22 | */
23 | private String htmlReport;
24 | /**
25 | * 测试任务执行时长,单位:毫秒
26 | */
27 | private Long sumTime;
28 | /**
29 | * 测试套/集 结果信息
30 | */
31 | private TngCount tngSuiteCount;
32 | /**
33 | * 测试套中所有测试集结果信息
34 | */
35 | private ArrayList tngTestCountList;//
36 | /**
37 | * 任务运行结果返回码
38 | */
39 | private int resNo;
40 | /**
41 | * 任务运行结果返回信息
42 | */
43 | private String resMsg;
44 |
45 |
46 | public String getHtmlReport() {
47 | return htmlReport;
48 | }
49 | public void setHtmlReport(String htmlReport) {
50 | this.htmlReport = htmlReport;
51 | }
52 | public TngCount getTngSuiteCount() {
53 | return tngSuiteCount;
54 | }
55 | public void setTngSuiteCount(TngCount tngSuiteCount) {
56 | this.tngSuiteCount = tngSuiteCount;
57 | }
58 | public ArrayList getTngTestCountList() {
59 | return tngTestCountList;
60 | }
61 | public void setTngTestCountList(ArrayList tngTestCountList) {
62 | this.tngTestCountList = tngTestCountList;
63 | }
64 | public String getTaskId() {
65 | return taskId;
66 | }
67 | public void setTaskId(String taskId) {
68 | this.taskId = taskId;
69 | }
70 | public String getTaskName() {
71 | return taskName;
72 | }
73 | public void setTaskName(String taskName) {
74 | this.taskName = taskName;
75 | }
76 | public Long getSumTime() {
77 | return sumTime;
78 | }
79 | public void setSumTime(Long sumTime) {
80 | this.sumTime = sumTime;
81 | }
82 | public int getResNo() {
83 | return resNo;
84 | }
85 | public void setResNo(int resNo) {
86 | this.resNo = resNo;
87 | }
88 | public String getResMsg() {
89 | return resMsg;
90 | }
91 | public void setResMsg(String resMsg) {
92 | this.resMsg = resMsg;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/report/TngCount.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.report;
2 |
3 | /**
4 | * 一个testng的测试集或测试套的执行结果信息
5 | * @author lianghui (lianghui@360.cn)
6 | */
7 | public class TngCount {
8 | /**
9 | * 测试套/集名称
10 | */
11 | private String name;
12 | /**
13 | * 所属的测试套的名称,为空标识无所属测试套
14 | */
15 | private String suiteName;
16 | /**
17 | * 失败数
18 | */
19 | private int failed;
20 | /**
21 | * 通过数
22 | */
23 | private int passed;
24 | /**
25 | * 跳过数
26 | */
27 | private int skipped;
28 | public String getName() {
29 | return name;
30 | }
31 | public void setName(String name) {
32 | this.name = name;
33 | }
34 | public String getSuiteName() {
35 | return suiteName;
36 | }
37 | public void setSuiteName(String suiteName) {
38 | this.suiteName = suiteName;
39 | }
40 | public int getFailed() {
41 | return failed;
42 | }
43 | public void setFailed(int failed) {
44 | this.failed = failed;
45 | }
46 | public int getPassed() {
47 | return passed;
48 | }
49 | public void setPassed(int passed) {
50 | this.passed = passed;
51 | }
52 | public int getSkipped() {
53 | return skipped;
54 | }
55 | public void setSkipped(int skipped) {
56 | this.skipped = skipped;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/run/TestRunInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.run;
2 |
3 | import java.util.List;
4 |
5 | import com.github.qihootest.leo.dispatch.testcase.ICase;
6 |
7 |
8 | /**
9 | * TestNG运行信息
10 | * @author lianghui (lianghui@360.cn)
11 | */
12 | public class TestRunInfo {
13 | private String taskName;//任务名称
14 | private String testng_OutPut;//TestNG输出目录
15 | private String htmlReportOutPath;//Html报告输出路径
16 | private String htmlReportTitle;//Html报告标题
17 | private List caseList;//用例信息
18 | public String getTaskName() {
19 | return taskName;
20 | }
21 | public void setTaskName(String taskName) {
22 | this.taskName = taskName;
23 | }
24 | public String getTestng_OutPut() {
25 | return testng_OutPut;
26 | }
27 | public void setTestng_OutPut(String testng_OutPut) {
28 | this.testng_OutPut = testng_OutPut;
29 | }
30 | public String getHtmlReportOutPath() {
31 | return htmlReportOutPath;
32 | }
33 | public void setHtmlReportOutPath(String htmlReportOutPath) {
34 | this.htmlReportOutPath = htmlReportOutPath;
35 | }
36 | public List getCaseList() {
37 | return caseList;
38 | }
39 | public void setCaseList(List caseList) {
40 | this.caseList = caseList;
41 | }
42 | public String getHtmlReportTitle() {
43 | return htmlReportTitle;
44 | }
45 | public void setHtmlReportTitle(String htmlReportTitle) {
46 | this.htmlReportTitle = htmlReportTitle;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/run/TestngRun.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.run;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.testng.ITestContext;
7 | import org.testng.TestListenerAdapter;
8 | import org.testng.TestNG;
9 |
10 | import com.github.qihootest.leo.dispatch.DispatchConf;
11 | import com.github.qihootest.leo.dispatch.report.ExportReportHtml;
12 | import com.github.qihootest.leo.dispatch.report.TestReport;
13 | import com.github.qihootest.leo.dispatch.report.TngCount;
14 | import com.github.qihootest.leo.dispatch.testcase.ICase;
15 | import com.github.qihootest.leo.toolkit.util.CommUtils;
16 | import com.github.qihootest.leo.toolkit.util.FileUtil;
17 | import com.github.qihootest.leo.toolkit.util.LogUtil;
18 |
19 | /**
20 | * 执行testng,
21 | * @author lianghui (lianghui@360.cn) 2012-10-29
22 | *
23 | */
24 | public class TestngRun {
25 | private LogUtil log=LogUtil.getLogger(TestngRun.class);//日志记录
26 | private TestNG tng;//运行TestNG
27 | private TestListenerAdapter listener;//运行的监听器
28 |
29 | private TestRunInfo runInfo;
30 | private List xmlFileList;
31 |
32 | private TestReport testReport;
33 |
34 | /*
35 | * 构造函数 初始化
36 | */
37 | public TestngRun() {
38 | tng=new TestNG();
39 | listener=new TestListenerAdapter();//定义监听器类型
40 | tng.addListener(listener);
41 | xmlFileList=new ArrayList<>();//记录测试使用的xml文件路径列表
42 | testReport=new TestReport();//记录测试报告测试报告信息
43 | runInfo=new TestRunInfo();
44 | }
45 |
46 | /**
47 | * 设置运行配置信息
48 | * @param runInfo
49 | */
50 | public void setRunInfo(TestRunInfo runInfo) {
51 | this.runInfo=runInfo;
52 | log.info(this.runInfo.getTaskName()+"配置任务信息成功");
53 | for (ICase icase : this.runInfo.getCaseList()) {
54 | addXmlFileList(icase.getCaseList());
55 | }
56 | }
57 |
58 | /**
59 | * 执行用例
60 | * @return boolean 执行成功返回true
61 | */
62 | public boolean run() {
63 | if (getXmlFileList().size()<1) {
64 | this.testReport.setResNo(-6000);
65 | this.testReport.setResMsg("Xml文件列表为空");
66 | log.error(getTaskName()+" :Xml文件列表为空");
67 | return false;
68 | }
69 | //运行相关参数配置
70 | //检查xlst文件,不存在则创建
71 | DispatchConf.writeConf();
72 | tng.setOutputDirectory(getTestNgOut());
73 |
74 | try {
75 | tng.setTestSuites(getXmlFileList());
76 | tng.run();
77 | } catch (Exception e) {
78 | // e.printStackTrace();
79 | this.testReport.setResNo(-7000);
80 | this.testReport.setResMsg("执行用例异常: "+e.getMessage());
81 | log.error(getTaskName()+" :执行用例异常--"+e.getMessage());
82 | return false;
83 | }
84 | //记录测试报告摘要
85 | createTestReport();
86 | //输出html测试报告
87 | if (!createHtmlReport()) {
88 | this.testReport.setResNo(1);
89 | this.testReport.setResMsg("任务执行成功,转换Html格式报告出错");
90 | log.info(getTaskName()+" :任务执行成功,转换Html格式报告出错");
91 | }else{
92 | this.testReport.setResNo(0);
93 | this.testReport.setResMsg("任务执行成功");
94 | log.info(getTaskName()+" :任务执行成功");
95 | }
96 | return true;
97 | }
98 |
99 | /**
100 | * 获取测试报告
101 | * @return TestReport
102 | */
103 | public TestReport getTestReport() {
104 | return this.testReport;
105 | }
106 |
107 | //私有方法
108 | private String getTestNgOut(){
109 | String out= this.runInfo.getTestng_OutPut();
110 | if (out == null || out.length()<1) {
111 | log.info("设置TestNG输出目录失败:"+this.runInfo.getTestng_OutPut());
112 | log.info("使用默认路径:"+DispatchConf.TestNgOutPath);
113 | return DispatchConf.TestNgOutPath;//默认的TestNG输出目录
114 | }
115 | if (!out.endsWith("/")) {
116 | out=out+"/";
117 | }
118 | return out;
119 | }
120 |
121 | private String getTaskName() {
122 | if (this.runInfo.getTaskName() == null || this.runInfo.getTaskName().length()<1) {
123 | String taskName = "未命名测试任务"+CommUtils.getRandomStr(5);
124 | log.info("设置任务名失败:"+this.runInfo.getTaskName());
125 | log.info("使用默认任务名:"+taskName);
126 | return taskName;//默认的TestNG输出目录
127 | }
128 | return this.runInfo.getTaskName();
129 | }
130 |
131 | private String getHtmlReportOut(){
132 | if (this.runInfo.getHtmlReportOutPath() == null || this.runInfo.getHtmlReportOutPath().length()<1) {
133 | log.info("设置Html输出目录失败:"+this.runInfo.getHtmlReportOutPath());
134 | log.info("使用默认Html输出目录:"+DispatchConf.HtmlReportOutPath);
135 | return DispatchConf.HtmlReportOutPath;//默认的html报告输出目录
136 | }
137 | return this.runInfo.getHtmlReportOutPath();
138 | }
139 |
140 | private String getHtmlReportTitle(){
141 | if (null == this.runInfo.getHtmlReportTitle() || this.runInfo.getHtmlReportTitle().length()<1) {
142 | log.info("设置Html报告标题失败:"+this.runInfo.getHtmlReportTitle());
143 | log.info("使用默认Html报告标题:"+DispatchConf.HtmlReportTitle);
144 | return DispatchConf.HtmlReportTitle;
145 | }
146 | return this.runInfo.getHtmlReportTitle();
147 | }
148 |
149 | private List getXmlFileList() {
150 | return xmlFileList;
151 | }
152 |
153 | private void addXmlFileList(List xmlFileList) {
154 | if (null==xmlFileList) {
155 | log.error("添加的Xml文件列表为null,添加失败");
156 | return;
157 | }
158 | for (String xmlFile : xmlFileList) {
159 | addXmlFile(xmlFile);
160 | }
161 | }
162 |
163 | private boolean addXmlFile(String xmlPathName){
164 | if (null==xmlPathName){
165 | log.error("添加的xml文件为null,添加失败");
166 | return false;
167 | }
168 | if (!FileUtil.getExtensionName(xmlPathName).equals("xml")) xmlPathName=xmlPathName+".xml";
169 |
170 | if (FileUtil.isExist(xmlPathName)) {
171 | log.info("执行队列添加xml文件成功:"+xmlPathName);
172 | this.xmlFileList.add(xmlPathName);
173 | return true;
174 | }else{
175 | log.error("添加的xml文件不存在:"+xmlPathName);
176 | return false;
177 | }
178 | }
179 |
180 |
181 | private boolean createHtmlReport() {
182 | if (ExportReportHtml.createHtmlReport(getTestNgOut()+"testng-results.xml",
183 | getHtmlReportOut(),getHtmlReportTitle())) {
184 |
185 | this.testReport .setHtmlReport(getHtmlReportOut());
186 | return true;
187 | }
188 | return false;
189 | }
190 |
191 | /**
192 | * 从监听器中获取需要的报告信息
193 | * @param listener
194 | */
195 | private void createTestReport() {
196 | log.info("从TestNG监听器中获取任务执行的用例信息");
197 | ArrayList testCountList=new ArrayList();;
198 | TngCount tngCount=new TngCount();
199 | tngCount.setName(getTaskName());
200 | tngCount.setFailed(this.listener.getFailedTests().size());
201 | tngCount.setPassed(this.listener.getPassedTests().size());
202 | tngCount.setSkipped(this.listener.getSkippedTests().size());
203 | this.testReport.setTngSuiteCount(tngCount);
204 | List testContextList=this.listener.getTestContexts();
205 | for (int i = 0; i < testContextList.size(); i++) {
206 | tngCount=new TngCount();
207 | tngCount.setName(testContextList.get(i).getName());
208 | tngCount.setSuiteName(testContextList.get(i).getSuite().getName());
209 | tngCount.setFailed(testContextList.get(i).getFailedTests().size());
210 | tngCount.setPassed(testContextList.get(i).getPassedTests().size());
211 | tngCount.setSkipped(testContextList.get(i).getSkippedTests().size());
212 |
213 | testCountList.add(tngCount);
214 | }
215 | this.testReport .setTaskName(getTaskName());
216 | this.testReport .setTngTestCountList(testCountList);
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/run/TestngRunSingle.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.run;
2 |
3 | import com.github.qihootest.leo.dispatch.report.TestReport;
4 | import com.github.qihootest.leo.toolkit.util.LogUtil;
5 |
6 | /**
7 | * 说明:封装用例执行 单例模式
8 | *
9 | * @author lianghui (lianghui@360.cn) 2012-12-04
10 | *
11 | */
12 | public class TestngRunSingle {
13 | private LogUtil log=LogUtil.getLogger(TestngRunSingle.class);//日志记录
14 | private static TestngRunSingle single;
15 | private static boolean Flag;//是否运行任务标识,为true时有任务在运行,false时无任务运行
16 | private TestRunInfo runInfo ;//任务名称
17 | private TestReport testReport;//执行完毕后,返回的测试报告信息
18 |
19 | //设置为单例模式
20 | private TestngRunSingle()
21 | {
22 | setFlag(false);
23 | init();//第一次创建时,初始化属性
24 | }
25 |
26 | /**
27 | * 运行前初始化相关信息
28 | */
29 | private void init(){
30 | log.info("TestNG执行实例创建");
31 | runInfo=new TestRunInfo();
32 | testReport=new TestReport();
33 | }
34 |
35 | /**
36 | * 获取类的实例
37 | * @return TestngRunSingle
38 | */
39 | public synchronized static TestngRunSingle getInstance() {
40 | if (single == null) {
41 | single = new TestngRunSingle();
42 | }
43 | return single;
44 | }
45 |
46 | /**
47 | * 设置任务信息
48 | * @param runInfo 任务配置信息
49 | */
50 | public void setRunInfo(TestRunInfo runInfo) {
51 | // 设置任务信息
52 | this.runInfo=runInfo;
53 | }
54 |
55 | /**
56 | * 获取当前任务运行标识
57 | * @return boolean
58 | */
59 | public boolean getFlag() {
60 | return Flag;
61 | }
62 |
63 | /**
64 | * 说明:执行任务返回测试报告信息
65 | * @return TestReport
66 | */
67 | public TestReport execTask() {
68 | setFlag(true);
69 | doTask();
70 | setFlag(false);
71 | return this.testReport;
72 | }
73 |
74 | //内部方法
75 | private static void setFlag(boolean flag) {
76 | Flag = flag;
77 | }
78 |
79 | /**
80 | * 说明:执行测试过程
81 | *
82 | * @return boolean
83 | */
84 | private boolean doTask() {
85 | log.info(this.runInfo.getTaskName()+" 开始执行");
86 | TestngRun testngRun = new TestngRun();
87 | testngRun.setRunInfo(runInfo);
88 | boolean result = testngRun.run();
89 | testReport = testngRun.getTestReport();
90 | testngRun = null;
91 | return result;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/CreateXmlFile.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import org.testng.xml.XmlClass;
8 | import org.testng.xml.XmlSuite;
9 | import org.testng.xml.XmlTest;
10 |
11 | import com.github.qihootest.leo.dispatch.DispatchConf;
12 | import com.github.qihootest.leo.toolkit.util.CommUtils;
13 | import com.github.qihootest.leo.toolkit.util.FileUtil;
14 | import com.github.qihootest.leo.toolkit.util.LogUtil;
15 |
16 |
17 | /**
18 | * 根据java文件输出Xml文件
19 | * @author lianghui (lianghui@360.cn)
20 | *
21 | */
22 | public class CreateXmlFile {
23 | private LogUtil log=LogUtil.getLogger(CreateXmlFile.class);//日志记录
24 |
25 | private XmlSuite suite;
26 | private String xmlFileFolder;
27 | private String suiteName;
28 |
29 | /**
30 | * 构造函数
31 | */
32 | public CreateXmlFile(){
33 | suite=new XmlSuite();
34 | setSuiteName("测试套件"+CommUtils.getRandomStr(5));
35 | setXmlFileFolder(DispatchConf.SuitsXmlPath);
36 | }
37 |
38 | /**
39 | * 构造函数
40 | * @param threadCont 线程数
41 | * @param setParallel 设置多线程方式
42 | */
43 | public CreateXmlFile(int threadCont,String setParallel){
44 | suite=new XmlSuite();
45 | setSuiteName("测试套件"+CommUtils.getRandomStr(5),threadCont,setParallel);
46 | setXmlFileFolder(DispatchConf.SuitsXmlPath);
47 | }
48 |
49 | public String getXmlFilePath(){
50 | if (suite.getTests().size()<1) {
51 | log.error("未添加任何测试集,未生成xml文件");
52 | return null;
53 | }else{
54 | return createXmlFile();
55 | }
56 | }
57 |
58 | /**
59 | * 添加java用例
60 | * @param cls
61 | */
62 | public void addJavaCase(Class> cls){
63 | if (null==cls) return ;
64 | String caseName=cls.getSimpleName();
65 | addJavaCase(caseName, cls);
66 | }
67 | /**
68 | * 添加java用例
69 | * @param caseName 用例名称
70 | * @param cls 类
71 | */
72 | public void addJavaCase(String caseName,Class> cls) {
73 | if (null==cls ) {
74 | log.error("添加的java类为null,添加失败");
75 | return;
76 | }
77 | if (null==caseName || caseName.length()<1) {
78 | log.info("指定的测试集为空:"+caseName);
79 | String nameTmp="未命名测试集"+CommUtils.getRandomStr(5);
80 | log.info("使用默认测试集名称:"+nameTmp);
81 | addClassToXmlTest(cls.getName(), nameTmp);
82 | return;
83 | }
84 | addClassToXmlTest(cls.getName(), caseName);
85 | }
86 |
87 | /**
88 | * 设置测试套名称
89 | * @param suiteName
90 | */
91 | public void setSuiteName(String suiteName) {
92 | if (null!=suiteName && suiteName.length()>0) {
93 | this.suiteName = suiteName;
94 | }else{
95 | log.info("设置的测试套件名称为空");
96 | log.info("使用默认测试套名称:"+this.suiteName);
97 | }
98 | suite.setName(this.suiteName);
99 | suite.setVerbose(1);
100 | }
101 |
102 | /**
103 | * 设置测试套名称
104 | * @param suiteName
105 | * @param threadCont 执行的线程数
106 | * @param setParallel 设置多线程的方式
107 | */
108 | public void setSuiteName(String suiteName,int threadCont,String setParallel) {
109 | this.setSuiteName(suiteName);
110 | if(threadCont>0){
111 | suite.setParallel(setParallel); //设置多线程的方式 methods ,tests,classes
112 | suite.setThreadCount(threadCont); //设置多线程的个数
113 | }
114 | }
115 |
116 | public void setXmlFileFolder(String xmlFileFolder) {
117 | this.xmlFileFolder = xmlFileFolder;
118 | }
119 | private void addClassToXmlTest(String pkgAndClsName,String testName) {
120 | XmlTest xmltest=new XmlTest();
121 | XmlClass classe=new XmlClass(pkgAndClsName);
122 | xmltest.setName(testName);
123 | xmltest.setClasses(Arrays.asList(classe));
124 | addTest(xmltest);
125 | }
126 |
127 | private void addTest(XmlTest test){
128 | if (null==test){
129 | log.error("测试集为null,添加失败");
130 | return;
131 | }
132 | suite.addTest(test);
133 | }
134 |
135 | /**
136 | * 生成testNG调用的xml文件
137 | * @return
138 | */
139 | private String createXmlFile(){
140 | List arr=new ArrayList();
141 | String xml=this.suite.toXml();
142 | String xx[]=xml.split("\n");
143 |
144 | for (int i = 0; i < xx.length; i++) {
145 | if (xx[i].indexOf("verbose")!=-1) {
146 | String temp=xx[i].trim();
147 | xx[i]=temp.substring(0, temp.indexOf("verbose")+12)+" preserve-order=\"true\" "
148 | +temp.substring(temp.indexOf("verbose")+12, temp.length());
149 | }
150 | arr.add(xx[i]);
151 | }
152 | String xmlName = getXmlFileFolder()+getSuiteName()+".xml";
153 | if (FileUtil.writeString(arr, xmlName, "UTF-8")){
154 | log.info("生成Xml文件成功:"+xmlName);
155 | return xmlName;
156 | }else {
157 | log.error("生成Xml文件失败:"+xmlName);
158 | return null;
159 | }
160 | }
161 |
162 | private String getSuiteName(){
163 | return this.suiteName;
164 | }
165 |
166 | private String getXmlFileFolder() {
167 | return xmlFileFolder;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/DataFileCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 |
4 | /**
5 | * 数据文件类型的测试用例
6 | *
7 | * @author lianghui (lianghui@360.cn)
8 | *
9 | */
10 | public class DataFileCase extends SuperCase{
11 | /**
12 | * 默认构造函数
13 | */
14 | public DataFileCase() {
15 | super();
16 | }
17 | //逻辑处理 输出xml配置文件
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/ICase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * 测试集(一个或多个测试集、测试套)
7 | * @author lianghui (lianghui@360.cn)
8 | *
9 | */
10 | public interface ICase {
11 |
12 | /**
13 | * 获取用例的xml文件路径信息列表
14 | * @return List
15 | */
16 | public List getCaseList();
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/JavaFileCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import com.github.qihootest.leo.toolkit.util.CommUtils;
7 | import com.github.qihootest.leo.toolkit.util.LogUtil;
8 |
9 | /**
10 | * .java文件类型的测试用例
11 | * @author lianghui (lianghui@360.cn)
12 | *
13 | */
14 | public class JavaFileCase extends SuperCase{
15 | private LogUtil log=LogUtil.getLogger(JavaFileCase.class);//日志记录
16 | private CreateXmlFile createXmlFile;
17 | /**
18 | * 构造函数
19 | */
20 | public JavaFileCase(){
21 | super();
22 | createXmlFile = new CreateXmlFile();
23 | }
24 |
25 | /**
26 | * 构造函数
27 | * @param threadCont 线程数
28 | */
29 | public JavaFileCase(int threadCont,String setParallel){
30 | super();
31 | createXmlFile = new CreateXmlFile(threadCont,setParallel);
32 | }
33 |
34 | /**
35 | * 获取用例列表,返回的是xml文件路径信息
36 | * @return List
37 | */
38 | public List getCaseList(){
39 | String xmlFilePath = createXmlFile.getXmlFilePath();
40 | if (null!=xmlFilePath){
41 | return Arrays.asList(xmlFilePath);
42 | }else{
43 | return null;
44 | }
45 |
46 | }
47 | /**
48 | * 添加用例
49 | * @param cls
50 | */
51 | public void addCase(Class> cls){
52 | if (null==cls) {
53 | log.error("添加的java用例类为null");
54 | return ;
55 | }
56 | String caseName=cls.getSimpleName();
57 | addCase(caseName, cls);
58 | }
59 | /**
60 | * 添加用例
61 | * @param caseName
62 | * @param cls
63 | */
64 | public void addCase(String caseName,Class> cls) {
65 | if (null==cls ) {
66 | log.error("添加的java用例类为null");
67 | return;
68 | }
69 | if (null==caseName || caseName.length()<1) {
70 | log.info("设置的用例名为空");
71 | caseName="未命名测试集"+CommUtils.getRandomStr(5);
72 | log.info("使用默认名称:"+caseName);
73 | }
74 | createXmlFile.addJavaCase(caseName, cls);
75 | }
76 |
77 | /**
78 | * 设置测试套名称 未设置使用默认名称 "未命名测试用例"+5随机字符
79 | * @param suiteName
80 | */
81 | public void setSuiteName(String suiteName) {
82 | createXmlFile.setSuiteName(suiteName);
83 | }
84 |
85 | /**
86 | * 设置生成的xml文件存放文件夹 未设置使用默认 qtaf/dispatch/suites/
87 | * @param suiteName
88 | */
89 | public void setXmlFileFolder(String xmlFileFolder) {
90 | createXmlFile.setXmlFileFolder(xmlFileFolder);
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/SuperCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * 测试用例基类
8 | * @author lianghui (lianghui@360.cn)
9 | *
10 | */
11 | public abstract class SuperCase implements ICase {
12 |
13 | protected List xmlPathNameList;
14 | /**
15 | * 构造函数
16 | */
17 | public SuperCase(){
18 | xmlPathNameList=new ArrayList<>();
19 | }
20 | /**
21 | * 返回用例列表 xml文件信息
22 | * @return List
23 | */
24 | public List getCaseList() {
25 | return this.xmlPathNameList;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/dispatch/testcase/XmlFileCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.dispatch.testcase;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.github.qihootest.leo.toolkit.util.LogUtil;
7 |
8 | /**
9 | * XML文件类型的测试用例
10 | * @author lianghui (lianghui@360.cn)
11 | *
12 | */
13 | public class XmlFileCase extends SuperCase{
14 |
15 | private LogUtil log=LogUtil.getLogger(XmlFileCase.class);//日志记录
16 | /**
17 | *默认构造函数
18 | */
19 | public XmlFileCase() {
20 | super();
21 | xmlPathNameList=new ArrayList<>();
22 | }
23 | /**
24 | * 构造函数 根据xml文件列表信息创建
25 | * @param xmlPathNameList
26 | */
27 | public XmlFileCase(List xmlPathNameList) {
28 | super();
29 | xmlPathNameList=new ArrayList<>();
30 | this.xmlPathNameList=xmlPathNameList;
31 | }
32 | /**
33 | * 构造函数 根据单个xml文件信息创建
34 | * @param xmlPathName
35 | */
36 | public XmlFileCase(String xmlPathName) {
37 | super();
38 | xmlPathNameList=new ArrayList<>();
39 | if (null==xmlPathName)return;
40 | this.xmlPathNameList.add(xmlPathName);
41 | }
42 |
43 | /**
44 | * 添加xml文件列表信息
45 | * @param xmlPathNameList
46 | */
47 | public void addXmlCase(List xmlPathNameList) {
48 | this.xmlPathNameList.addAll(xmlPathNameList);
49 | }
50 | /**
51 | * 添加单个xml文件信息
52 | * @param xmlPathName
53 | */
54 | public void addXmlCase(String xmlPathName) {
55 | if (null==xmlPathName){
56 | log.error("添加的Xml文件为null,添加失败");
57 | return;
58 | }
59 | this.xmlPathNameList.add(xmlPathName);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/IftConf.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.InputStreamReader;
9 | import java.io.OutputStream;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.Properties;
13 |
14 | import org.apache.commons.io.FileUtils;
15 |
16 | /**
17 | * 接口全局配置文件
18 | * @author @bwgang (bwgang@163.com)
19 | *
20 | */
21 | public class IftConf{
22 | /**
23 | * 自定义依赖参数
24 | */
25 | public static Map DependPara = new HashMap();
26 | /**
27 | * 项目根目录
28 | */
29 | public static final String RootPath = getRootPath();
30 | /**
31 | * 接口ift配置等文件默认保存目录
32 | */
33 | public static String IftPath = RootPath+"leo/ift/";
34 | /**
35 | * 默认编译输出目录
36 | */
37 | public static String DistPath = getDistPath();
38 | /**
39 | * 默认lib目录
40 | */
41 | public static String LibPath = getLibPath();
42 | /**
43 | * jar文件目录 maven
44 | */
45 | public static String JarFile = "";
46 | /**
47 | * 默认生成的java文件存放目录
48 | */
49 | public static String JavaPath = IftPath+"javaCase/";
50 |
51 | /**
52 | * 默认生成java文件时的包名
53 | */
54 | public static String PackageName = "com.github.qihootest.leo.ift.testcase";
55 |
56 | /**
57 | * 默认的模板文件
58 | */
59 | public static String TemplatePath = IftPath +"template/Template.ftl";
60 |
61 | /**
62 | * 默认测试报告输出路径
63 | */
64 | public static String ReportPath=IftPath+"report/";
65 | /**
66 | * 默认的配置文件
67 | */
68 | public static String ConfFile = IftPath + "config/IftConf.properties";
69 |
70 | //代理相关配置信息
71 | /**
72 | * 是否启用代理配置
73 | */
74 | public static String ProxyEnable = getPropValue("ProxyEnable","N");
75 | /**
76 | * 代理IP
77 | */
78 | public static String ProxyIp = getPropValue("ProxyIp","127.0.0.1");
79 | /**
80 | * 代理端口
81 | */
82 | public static int PROXY_PORT = Integer.parseInt(getPropValue("ProxyPort","8888"));
83 |
84 | //https协议配置
85 | /**
86 | * 是否使用本地认证信息
87 | */
88 | public static String SSL = getPropValue("SSL","N");
89 | /**
90 | * 本地认证信息路径
91 | */
92 | public static String KeyPath = getPropValue("keyPath",IftConf.RootPath);
93 | /**
94 | * 密匙库的密码
95 | */
96 | public static String KeyPassword = getPropValue("keyPassword","");
97 |
98 | //时间样式设置
99 | public static String DateFormat = getPropValue("dateFormat","yyyMMddHHmmss");
100 |
101 | //结果比对参数
102 | /**
103 | * json默认解析方式 单层解析
104 | */
105 | public static final int parseJson = 1;
106 |
107 | //测试用例Excel文件读取 相关配置信息
108 | /**
109 | * url所在行数
110 | */
111 | public static final int urlRow = 0;
112 | /**
113 | * url所在列数
114 | */
115 | public static final int urlCol = 1;
116 | /**
117 | * httpMethod所在行数
118 | */
119 | public static final int methodRow = 1;
120 | /**
121 | * httpMethod所在列数
122 | */
123 | public static final int methodCol = 1;
124 | /**
125 | * 全局cookie所在行数
126 | */
127 | public static final int cookieRow = 2;
128 | /**
129 | * 全局cookie所在列数
130 | */
131 | public static final int cookieCol = 1;
132 | /**
133 | * 参数签名计算参数个数所在行数
134 | */
135 | public static final int argCountRow = 3;
136 | /**
137 | * 参数签名计算参数个数所在列数
138 | */
139 | public static final int argCountCol = 1;
140 | /**
141 | * 标题所在行
142 | */
143 | public static final int typeRow = 4;
144 | /**
145 | * 用例数据开始行数
146 | */
147 | public static final int paramStartRow = 5;
148 | /**
149 | * 用例数据开始列表
150 | */
151 | public static final int paramStartCol = 3;
152 | /**
153 | * caseId所在列数
154 | */
155 | public static final int caseIdCol = 1;
156 | /**
157 | * 是否执行属性所在列数
158 | */
159 | public static final int isRunCol = 1;
160 | /**
161 | * secondUrl所在列数
162 | */
163 | public static final int secondUrlCol = 2;
164 |
165 |
166 |
167 | /**
168 | * 删除默认临时目录
169 | */
170 | public static void delTmpPath() {
171 | try {
172 | // 清空java文件生成目录
173 | FileUtils.deleteDirectory(new File(JavaPath));
174 | } catch (IOException e) {
175 | // e.printStackTrace();
176 | }
177 | }
178 |
179 |
180 |
181 | /**
182 | * 如果配置文件不存在,写入
183 | * @return boolean 已存在或创建失败时返回false 创建成功返回true
184 | */
185 | public static boolean writeConf() {
186 | //相应配置文件如果不存在,则创建
187 | boolean flag =false;
188 | if (!new File(ConfFile).exists()){
189 | flag = copyFile(IftConf.class.getResourceAsStream("/IftConf.properties"),new File(ConfFile));
190 | }
191 | if (!new File(TemplatePath).exists()){
192 | flag = copyFile(IftConf.class.getResourceAsStream("/Template.ftl"),new File(TemplatePath));
193 | }
194 | return flag;
195 | }
196 |
197 | /**
198 | * 设置依赖的jar文件路径信息 maven
199 | * @param args
200 | * @return boolean 设置成功返回true
201 | */
202 | public static boolean updateJarFile(String[] args) {
203 |
204 | if (IftConf.JarFile.length()>0) {
205 | return true;//JarFile已设置
206 | }
207 | try {
208 | if (null!=args && args.length>0) {//设置JarFile,同时写入文件
209 | JarFile=args[0];
210 | FileUtils.writeStringToFile(new File(IftConf.IftPath+"JarFile"), JarFile, "UTF-8");
211 | }else{
212 | if (new File(IftPath+"JarFile").exists()) {//已存在则读取
213 | FileUtils.readFileToString(new File(IftPath+"JarFile"), "UTF-8");
214 | IftConf.JarFile=FileUtils.readFileToString(new File(IftPath+"JarFile"), "UTF-8");
215 | }else{
216 | System.out.print("在eclipse中,第一次需要以maven方式执行");
217 | return false;
218 | }
219 | }
220 | } catch (IOException e) {
221 | // e.printStackTrace();
222 | return false;
223 | }
224 | return true;
225 | }
226 |
227 |
228 | /**
229 | * 获取项目根目录,如果是在Tomcat中运行,则返回部署根目录
230 | * @return String
231 | */
232 | private static String getRootPath() {
233 | String path="";
234 | if (IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF")!=-1) {
235 | path=IftConf.class.getClassLoader().getResource("").toString().substring(6, IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF"));
236 | path=path+"/";
237 | }else {
238 | String temp=System.getProperty("user.dir");
239 | temp=temp.replace("\\","/");
240 | path=temp+"/";
241 | }
242 | return path;
243 | }
244 |
245 | /**
246 | * 获取编译输出目录,如果是在Tomcat中运行,则返回部署目录下的WEB-INF/classes/ 否则返回bin/
247 | * @return String
248 | */
249 | private static String getDistPath() {
250 | String path="";
251 | if (IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF")!=-1) {
252 | path=IftConf.class.getClassLoader().getResource("").toString().substring(6, IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF"));
253 | path=path+"/WEB-INF/classes/";
254 | }else {
255 | // path = GlobalSettings.class.getClassLoader().getResource("").toString();
256 | String temp=System.getProperty("user.dir");
257 | temp=temp.replace("\\","/");
258 | path=temp+"/target/classes/";
259 | }
260 | return path;
261 | }
262 |
263 | /**
264 | * 获取jar包存放的lib目录,如果是在Tomcat中运行,则返回部署目录下的WEB-INF/lib/ 否则返回lib/
265 | * @return String
266 | */
267 | private static String getLibPath() {
268 | String path="";
269 | if (IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF")!=-1) {
270 | path=IftConf.class.getClassLoader().getResource("").toString().substring(6, IftConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF"));
271 | path=path+"/WEB-INF/lib/";
272 | }else {
273 | // path = GlobalSettings.class.getClassLoader().getResource("").toString();
274 | String temp=System.getProperty("user.dir");
275 | temp=temp.replace("\\","/");
276 | path=temp+"/lib/";
277 | }
278 | return path;
279 | }
280 |
281 | private static String getPropValue(String key, String defaultValue) {
282 | return getProperties().getProperty(key, defaultValue);
283 | }
284 |
285 | private static Properties getProperties() {
286 | Properties prop = new Properties();
287 | try {
288 | //配置文件不存在则创建
289 | IftConf.writeConf();
290 | FileInputStream file = new FileInputStream(ConfFile);
291 | prop.load(new InputStreamReader(file,"UTF-8"));
292 | file.close();
293 | } catch (Exception e) {
294 | // e.printStackTrace();
295 | }
296 | return prop;
297 | }
298 |
299 | private static boolean copyFile(InputStream from, File to) {
300 | try {
301 | if (! to.getParentFile().exists()) {
302 | to.getParentFile().mkdirs();
303 | }
304 | OutputStream os = new FileOutputStream(to);
305 | byte[] buffer = new byte[65536];
306 | int count = from.read(buffer);
307 | while (count > 0) {
308 | os.write(buffer, 0, count);
309 | count = from.read(buffer);
310 | }
311 | os.close();
312 | return true;
313 | } catch (IOException e) {
314 | // e.printStackTrace();
315 | return false;
316 | }
317 |
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/IftExec.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | */
4 | package com.github.qihootest.leo.ift;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import com.github.qihootest.leo.dispatch.DispatchConf;
10 | import com.github.qihootest.leo.dispatch.ExecTask;
11 | import com.github.qihootest.leo.dispatch.report.TestReport;
12 | import com.github.qihootest.leo.dispatch.run.TestRunInfo;
13 | import com.github.qihootest.leo.dispatch.testcase.ICase;
14 | import com.github.qihootest.leo.ift.testcase.autocreate.IftDataFileCase;
15 |
16 | /**
17 | * 接口测试任务执行类
18 | * @author @bwgang (bwgang@163.com)
19 | *
20 | */
21 | public class IftExec {
22 |
23 | private ExecTask exec;
24 | private TestRunInfo runInfo;
25 | private List caseList;
26 | private IftDataFileCase dataCase;
27 |
28 | /**
29 | * 构造函数
30 | */
31 | public IftExec(){
32 |
33 | //清空临时目录
34 | DispatchConf.delTmpPath();
35 | IftConf.delTmpPath();
36 | exec = new ExecTask();
37 | runInfo = new TestRunInfo();
38 | caseList = new ArrayList<>();//用例列表
39 | dataCase = new IftDataFileCase();
40 | dataCase.setIftTaskName("接口测试");
41 | }
42 |
43 | /**
44 | * 构造函数
45 | * @param threadCont
46 | */
47 | public IftExec(int threadCont,String setParallel){
48 |
49 | //清空临时目录
50 | DispatchConf.delTmpPath();
51 | IftConf.delTmpPath();
52 | exec = new ExecTask();
53 | runInfo = new TestRunInfo();
54 | caseList = new ArrayList<>();//用例列表
55 | dataCase = new IftDataFileCase(setParallel,threadCont);
56 | dataCase.setIftTaskName("接口测试");
57 | }
58 |
59 | /**
60 | * 添加用例
61 | * @param casePath 用例路径 必填
62 | * @param sheetName Excel的sheet表名 可选
63 | * @param caseName 用例名称 必填
64 | * @param cls 执行用例的类 必填
65 | * @param method 类中的方法 必填
66 | */
67 | public void addCase(String casePath, String sheetName, String caseName,
68 | Class> cls,String method){
69 | dataCase.addCase(casePath, sheetName, caseName, cls, method);
70 | }
71 |
72 | /**
73 | * 任务执行
74 | * @return TestReport
75 | */
76 | public TestReport run(){
77 | caseList.add(dataCase);
78 | //设置运行配置信息
79 | runInfo.setTaskName(dataCase.getTaskName());//任务名称
80 | runInfo.setCaseList(caseList);//用例
81 | runInfo.setHtmlReportOutPath(dataCase.getHtmlReportPath());//设置测试报告输出目录,
82 |
83 | //可选运行参数设置
84 | // runInfo.setTestng_OutPut(IftConf.IftPath+"testng-out/");//设置TestNG输出目录,--可选
85 | // runInfo.setHtmlReportOutPath(IftConf.IftPath+"report/");//设置测试报告输出目录,---可选
86 | // runInfo.setHtmlReportTitle("设置测试报告标题-可选");//设置测试报告标题 ---可选
87 | // TestngLog.setOutputTestNGLog(false);//不记录TestNG日志,--可选
88 |
89 | //执行
90 | exec.setRunInfo(runInfo);
91 | return exec.Exec();
92 | }
93 |
94 | /**
95 | * 返回Excel报告的路径
96 | * @return String
97 | */
98 | public String getExcelReportPath() {
99 | return dataCase.getExcelReportPath();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/core/CompareResult.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.core;
2 |
3 | import java.util.Iterator;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 | import java.util.TreeMap;
11 |
12 | import com.github.qihootest.leo.ift.IftConf;
13 | import com.github.qihootest.leo.toolkit.util.CommUtils;
14 | import com.github.qihootest.leo.toolkit.util.JsonUtil;
15 | import com.github.qihootest.leo.toolkit.util.StringUtil;
16 | import com.github.qihootest.leo.toolkit.util.XmlUtil;
17 |
18 |
19 | /**
20 | * 说明:比对期望结果与实际结果
21 | *
22 | * @author lianghui (lianghui@360.cn)
23 | *
24 | */
25 | public class CompareResult {
26 |
27 | private String clearActres;
28 | private String clearExpres;
29 |
30 | /**
31 | * 默认构造函数
32 | */
33 | public CompareResult() {
34 | clearActres = "";
35 | clearExpres = "";
36 | }
37 |
38 | /**
39 | * 说明:期望结果与实际结果的比对
40 | * @param expRes 预期结果
41 | * @param actRes 从请求响应中提取过滤后的实际结果
42 | * @param config 可选参数默认为1,只解析一层,2时全解析
43 | * @return boolean 相同时返回true,不同时返回false
44 | */
45 | public boolean getCompareResult(String expRes, String actRes,int config){
46 | //开始比对之前,清空已整理后的预期与实际结果字符串
47 | setClearActres("");
48 | setClearExpres("");
49 | //预期结果中匹配${}
50 | Pattern pattern = Pattern.compile("^\\$\\{(.*)\\}$");
51 | Matcher matcher = pattern.matcher(expRes);
52 | Map exp = new TreeMap();
53 | Map act = new TreeMap();
54 | act = trimActres(actRes,config); //将实际结果解析成map
55 | if (StringUtil.IsNullOrEmpty(expRes)) {
56 | setClearExpres("预期结果为null或空字符串,不进行比对");
57 | setClearActres("未设置预期值&实际结果为:"+actRes);
58 | return true;//预期结果为空或null时,不再进行比对处理,直接返回true
59 | }else if(matcher.matches()){
60 | String para = matcher.group(1);
61 | if(para.contains("_")){ //当依赖参数中有多个值时进行处理(同一个id可能会有多个依赖参数,用_进行分割)
62 | String[] paraArray = para.split("_");
63 | para = paraArray[0];
64 | }
65 | if(act.get(para)!=null){ //实际结果中没有预期结果参数,则用例失败
66 | setClearExpres("预期结果为依赖参数,不进行验证");
67 | setClearActres("预期结果为依赖参数&实际结果为:"+actRes);
68 | IftConf.DependPara.put(matcher.group(1), act.get(para)); //添加依赖参数(赋值)
69 | return true;
70 | }else{ //当未匹配到依赖参数时返回 false
71 | setClearExpres("预期结果为依赖参数:"+expRes);
72 | setClearActres("实际结果未匹配到参数:"+para);
73 | return false;
74 | }
75 |
76 | }
77 | exp = trimExpres(expRes); //将预期结果解析成map
78 | if (StringUtil.IsNullOrEmpty(actRes)) {
79 | setClearActres("实际结果为null或空字符串,未找到");
80 | return false;//实际结果为空或null时,不再进行比对处理,直接返回false
81 | }
82 | return compareMap(exp, act);
83 | }
84 |
85 | /**
86 | * 说明:期望结果与实际结果的比对 json解析方式 使用默认
87 | * @param expRes 预期结果
88 | * @param actRes 从请求响应中提取过滤后的实际结果
89 | * @return boolean 相同时返回true,不同时返回false
90 | */
91 | public boolean getCompareResult(String expRes, String actRes) {
92 | return getCompareResult(expRes,actRes,IftConf.parseJson);
93 | }
94 |
95 | /**
96 | * 说明:比较两个map
97 | * @param expMap
98 | * @param actMap
99 | * @return boolean 如果map1∩map2等于map1或者map2,则返回true,否则返回false
100 | */
101 | private boolean compareMap(Map expMap,Map actMap) {
102 | List listFlag = new LinkedList();
103 | String record = "";// 记录在实际结果中查找到的结果,记录格式为key=value&key=value.....
104 |
105 | // 遍历预期结果map表
106 | for (Entry entryExp : expMap.entrySet()) {
107 | String expKey = entryExp.getKey();
108 | String expValue = entryExp.getValue();
109 |
110 | boolean flag=false;//在实际结果中是否找到对应的key-value
111 |
112 | //遍历实际结果map表
113 | for (Entry entryAct : actMap.entrySet()) {
114 | String actKey = entryAct.getKey();
115 | String actValue = entryAct.getValue();
116 | if (actKey.equals(expKey)) {//在实际结果中找到对应的key-value
117 | // 记录每个键值的比对结果
118 | if (CompareStr(expValue, actValue)) {
119 | listFlag.add(1);
120 | } else {
121 | listFlag.add(0);
122 | }
123 | // 记录在实际结果中找到的记录
124 | record += actKey + "=" + actValue + "&";
125 | flag=true;
126 | break;//比对完毕,结束实际结果map表遍历
127 | }
128 | }//实际结果map表遍历结束
129 |
130 | //在实际结果map表中未找到对应的key-value时的处理
131 | if (false==flag) {
132 | record +=expKey+"的值未找到";
133 | listFlag.add(0);
134 | }
135 |
136 | }// 预期结果map表遍历结束
137 |
138 | // 更新整理后实际结果的值
139 | if (record.length() > 2) {
140 | if (record.substring(record.length() - 1, record.length()).equals("&")) {
141 | setClearActres(record.substring(0, record.length() - 1));
142 | }else{
143 | setClearActres(record+"未找到");
144 | }
145 | }
146 |
147 | if (record.indexOf("未找到")>-1) {
148 | setClearActres(record+"&实际结果为:"+StringUtil.getStrFromMap(actMap));
149 | }
150 |
151 | // 汇总比对结果
152 | int sum = 1;
153 | for (int i = 0; i < listFlag.size(); i++) {
154 | sum *= listFlag.get(i);
155 | }
156 | // 返回比对结果
157 | if (sum == 1) {
158 | return true;
159 | } else {
160 | return false;
161 | }
162 | }
163 |
164 | /**
165 | * 单个预期结果值与对应实际结果值的比对,支持预期结果值以#标识分割,多个预期结果的处理
166 | * @param expValue
167 | * @param actValue
168 | * @return boolean
169 | * @author lianghui
170 | */
171 | private boolean CompareStr(String expValue, String actValue) {
172 | // 预期结果与实际结果任一为null,返回false
173 | if (null == actValue || null == expValue) return false;
174 | // 判断实际结果是否为数组格式
175 | if (actValue.startsWith("[") && actValue.endsWith("]")) {
176 | String[] actValueList = StringUtil.stringToArray(actValue);
177 | // 判断预期结果是否来数组格式(预期结果包含[])
178 | if (expValue.startsWith("[") && expValue.endsWith("]")) {
179 | String[] expArray = StringUtil.stringToArray(expValue);
180 | if (actValueList.length == expArray.length) { // 判断实际结果与预期结果的数组长度是否相同,不相同则直接返回错误
181 | for (int i = 0; i < actValueList.length; i++) {
182 | if (!(actValueList[i].trim())
183 | .equals(expArray[i].trim())) { // 判断实际结果与预期结果中的数据组是否相同
184 | return false;
185 | }
186 | }
187 | // 预期结果与实际结果的数组长度不相等,则直接返回false
188 | } else {
189 | return false;
190 | }
191 | // 预期结果为非数组,则判断预期结果是否包含在实际结果中
192 | } else {
193 | int len = actValueList.length;
194 | for (int i = 0; i < len; i++) {
195 | if ((actValueList[i].trim()).equals(expValue.trim())) {
196 | return true;
197 | }
198 |
199 | }
200 | }
201 | return true;
202 | } else {
203 |
204 | // 判断是否有多个预期结果值(预期结果中包含#,进入下面方法)
205 | if (expValue.contains("#")) {
206 | String[] allExpValue2 = expValue.split("#");
207 | for (int i = 0; i < allExpValue2.length; i++) {
208 | if (actValue.equals(allExpValue2[i])) {
209 | return true;
210 | }
211 | }
212 | return false;// 返回结果
213 | }
214 |
215 | // 仅1个预期结果值,并对int关键词做处理
216 | if (expValue.contains("int")) { // 预期结果中有int值
217 | if (actValue.matches("[0-9]+") & !actValue.equals("0")) { // 匹配int类型实际结果,但实际结果不能为0
218 | return true;
219 | } else {
220 | return false;
221 | }
222 | } else if (!actValue.equalsIgnoreCase(expValue)) { // 预期结果中没有int值
223 | return false;
224 | } else {
225 | return true;
226 | }
227 | }
228 |
229 | }
230 |
231 | /**
232 | * 说明:对预期结果的字符串进行清理,
233 | * @param expres预期结果字符串
234 | * @return Map 返回整理后的预期结果
235 | */
236 | private Map trimExpres(String expres) {
237 | Map trimExpres = new TreeMap();
238 | trimExpres = CommUtils.parseQuery(expres, '&', '=');
239 | if (null == trimExpres) {
240 | trimExpres = new TreeMap();
241 | setClearExpres("预期结果未找到,请检查:"+expres);
242 | return trimExpres;
243 | }
244 | String temp = "";
245 | int i = 0;
246 | for (Entry entry : trimExpres.entrySet()) {
247 | String key = entry.getKey().toString();
248 | String value = entry.getValue().toString();
249 | if (i == 0) {
250 | temp += key + "=" + value;
251 | } else {
252 | temp += "&" + key + "=" + value;
253 | }
254 | i++;
255 | }
256 | setClearExpres(temp);
257 | return trimExpres;
258 | }
259 |
260 | /**
261 | * 说明:对请求返回的实际结果字符串进行清理,
262 | * @param responseRes 实际结果字符串
263 | * @return Map ,返回整理后的实际结果
264 | */
265 | public Map trimActres(String responseRes,int config) {
266 | Map trimactres = new TreeMap();
267 | Map map = new TreeMap();
268 | XmlUtil xmlUtil = new XmlUtil();
269 | if (XmlUtil.isXmlText(responseRes)) {
270 | map = xmlUtil.fomatXMLToMap(responseRes);
271 | if (map.size()<1) {
272 | map.put("解析xml格式错误", "---"+responseRes);
273 | }
274 | } else{
275 | if(config == 1){//单层方式解析json串
276 | map = JsonUtil.getResult(responseRes);
277 | }else if(config == 2){//多层方式解析json串
278 | map = JsonUtil.getAllResult(responseRes);
279 | }else{//config不为1、2时 按单层方式解析
280 | map = JsonUtil.getResult(responseRes);
281 | }
282 | if(map == null){
283 | trimactres.put("解析json格式错误", "---"+responseRes);
284 | return trimactres;
285 | }
286 |
287 | }
288 | for (Iterator> it = map.entrySet().iterator(); it.hasNext();) { //object类型转换成String类型
289 | @SuppressWarnings("rawtypes")
290 | Map.Entry entity = it.next();
291 | trimactres.put(entity.getKey().toString(), entity.getValue().toString());
292 | }
293 | return trimactres;
294 | }
295 |
296 |
297 | public String getClearActres() {
298 | return clearActres;
299 | }
300 |
301 | public void setClearActres(String clearActres) {
302 | this.clearActres = clearActres;
303 | }
304 |
305 | public String getClearExpres() {
306 | return clearExpres;
307 | }
308 |
309 | public void setClearExpres(String clearExpres) {
310 | this.clearExpres = clearExpres;
311 | }
312 |
313 | }
314 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/core/IFtResultInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.core;
2 |
3 | import com.github.qihootest.leo.toolkit.httpclient.ResponseInfo;
4 |
5 |
6 | /**
7 | * 接口测试执行结果信息
8 | * @author lianghui (lianghui@360.cn)
9 | */
10 | public class IFtResultInfo {
11 | private ResponseInfo responseInfo;//http请求的返回信息
12 | private String expRes;//预期结果信息
13 | private String actRes;//整理后的实际结果信息
14 | private boolean compareRes;//预期与实际的比对结果 true或false
15 | public ResponseInfo getResponseInfo() {
16 | return responseInfo;
17 | }
18 | public void setResponseInfo(ResponseInfo responseInfo) {
19 | this.responseInfo = responseInfo;
20 | }
21 | public String getExpRes() {
22 | return expRes;
23 | }
24 | public void setExpRes(String expRes) {
25 | this.expRes = expRes;
26 | }
27 | public String getActRes() {
28 | return actRes;
29 | }
30 | public void setActRes(String actRes) {
31 | this.actRes = actRes;
32 | }
33 | public boolean getCompareRes() {
34 | return compareRes;
35 | }
36 | public void setCompareRes(boolean compareRes) {
37 | this.compareRes = compareRes;
38 | }
39 |
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/IftTestCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase;
2 |
3 | import java.util.ArrayList;
4 | import java.util.LinkedHashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.TreeMap;
8 |
9 | /**
10 | * 说明:测试用例实体类
11 | * @author lianghui (lianghui@360.cn)
12 | *
13 | */
14 | public class IftTestCase {
15 | private String url;//发起请求的url地址
16 | private String httpMethod;//请求的方法
17 | private int argCount; //参与签名计算的参数个数
18 | private String enCoding;//编码
19 | private String casesetName;//测试集名称
20 | private String templatePath;//使用的模板存储位置
21 | private String cookie;//此用例的cookie信息
22 | private String secretKey;//参与签名计算的密钥
23 | private String signKey;//标识签名值参数的key值
24 | private boolean signFlag;//标识是否计算签名,true--需要签名,false不需要签名
25 | private LinkedHashMap caseMap;//所有键值对信息
26 | private List paralist;//get请求的参数项列表
27 | private List formlist;//post请求的参数项列表
28 | private List signlist;//参与签名计算的参数项列表
29 | private List headerlist;//header的参数项列表
30 | private TreeMap headerMap;//发起请求时使用的header信息
31 | private Map results;//依据此用例发起请求后返回的结果串
32 |
33 | private String caseId;//用例的ID标识
34 | private String testPoint;//测试点
35 |
36 | /**
37 | * 默认构造函数
38 | */
39 | public IftTestCase() {
40 | this.url = "";
41 | this.httpMethod = "";
42 | this.argCount = 0;
43 | this.enCoding = "";
44 | this.casesetName = "";
45 | this.templatePath = "";
46 | this.cookie = "";
47 | this.secretKey = "";
48 | this.signFlag = false;
49 | this.caseMap = new LinkedHashMap();
50 | this.paralist = new ArrayList();
51 | this.formlist = new ArrayList();
52 | this.signlist = new ArrayList();
53 | this.headerlist = new ArrayList();
54 | this.headerMap = new TreeMap();
55 | this.results = new TreeMap();
56 | }
57 | /**
58 | * @return the url
59 | */
60 | public String getUrl() {
61 | return url;
62 | }
63 | /**
64 | * @param url the url to set
65 | */
66 | public void setUrl(String url) {
67 | this.url = url;
68 | }
69 | /**
70 | * @return the httpMethod
71 | */
72 | public String getHttpMethod() {
73 | return httpMethod;
74 | }
75 | /**
76 | * @param httpMethod the httpMethod to set
77 | */
78 | public void setHttpMethod(String httpMethod) {
79 | this.httpMethod = httpMethod;
80 | }
81 |
82 | /**
83 | * @return the argCount
84 | */
85 | public int getArgCount() {
86 | return argCount;
87 | }
88 | /**
89 | * @param argCount the argCount to set
90 | */
91 | public void setArgCount(int argCount) {
92 | this.argCount = argCount;
93 | }
94 | /**
95 | * @return the enCoding
96 | */
97 | public String getEnCoding() {
98 | return enCoding;
99 | }
100 | /**
101 | * @param enCoding the enCoding to set
102 | */
103 | public void setEnCoding(String enCoding) {
104 | this.enCoding = enCoding;
105 | }
106 | /**
107 | * @return the casesetName
108 | */
109 | public String getCasesetName() {
110 | return casesetName;
111 | }
112 | /**
113 | * @param casesetName the casesetName to set
114 | */
115 | public void setCasesetName(String casesetName) {
116 | this.casesetName = casesetName;
117 | }
118 | /**
119 | * @return the templatePath
120 | */
121 | public String getTemplatePath() {
122 | return templatePath;
123 | }
124 | /**
125 | * @param templatePath the templatePath to set
126 | */
127 | public void setTemplatePath(String templatePath) {
128 | this.templatePath = templatePath;
129 | }
130 | /**
131 | * @return the cookie
132 | */
133 | public String getCookie() {
134 | return cookie;
135 | }
136 | /**
137 | * @param cookie the cookie to set
138 | */
139 | public void setCookie(String cookie) {
140 | this.cookie = cookie;
141 | }
142 | /**
143 | * @return the secretKey
144 | */
145 | public String getSecretKey() {
146 | return secretKey;
147 | }
148 | /**
149 | * @param secretKey the secretKey to set
150 | */
151 | public void setSecretKey(String secretKey) {
152 | this.secretKey = secretKey;
153 | }
154 | /**
155 | * @return the signKey
156 | */
157 | public String getSignKey() {
158 | return signKey;
159 | }
160 | /**
161 | * @param signKey the signKey to set
162 | */
163 | public void setSignKey(String signKey) {
164 | this.signKey = signKey;
165 | }
166 | /**
167 | * @return the signFlag
168 | */
169 | public boolean isSignFlag() {
170 | return signFlag;
171 | }
172 | /**
173 | * @param signFlag the signFlag to set
174 | */
175 | public void setSignFlag(boolean signFlag) {
176 | this.signFlag = signFlag;
177 | }
178 |
179 | /**
180 | * @return the caseMap
181 | */
182 | public LinkedHashMap getCaseMap() {
183 | return caseMap;
184 | }
185 | /**
186 | * @param caseMap the caseMap to set
187 | */
188 | public void setCaseMap(LinkedHashMap caseMap) {
189 | this.caseMap = caseMap;
190 | }
191 | /**
192 | * @return the paralist
193 | */
194 | public List getParalist() {
195 | return paralist;
196 | }
197 | /**
198 | * @param paralist the paralist to set
199 | */
200 | public void setParalist(List paralist) {
201 | this.paralist = paralist;
202 | }
203 | /**
204 | * @return the formlist
205 | */
206 | public List getFormlist() {
207 | return formlist;
208 | }
209 | /**
210 | * @param formlist the formlist to set
211 | */
212 | public void setFormlist(List formlist) {
213 | this.formlist = formlist;
214 | }
215 | /**
216 | * @return the signlist
217 | */
218 | public List getSignlist() {
219 | return signlist;
220 | }
221 | /**
222 | * @param signlist the signlist to set
223 | */
224 | public void setSignlist(List signlist) {
225 | this.signlist = signlist;
226 | }
227 | /**
228 | * @return the headerlist
229 | */
230 | public List getHeaderlist() {
231 | return headerlist;
232 | }
233 | /**
234 | * @param headerlist the headerlist to set
235 | */
236 | public void setHeaderlist(List headerlist) {
237 | this.headerlist = headerlist;
238 | }
239 | /**
240 | * @return the headerMap
241 | */
242 | public TreeMap getHeaderMap() {
243 | return headerMap;
244 | }
245 | /**
246 | * @param headerMap the headerMap to set
247 | */
248 | public void setHeaderMap(TreeMap headerMap) {
249 | this.headerMap = headerMap;
250 | }
251 | /**
252 | * @return the results
253 | */
254 | public Map getResults() {
255 | return results;
256 | }
257 | /**
258 | * @param results the results to set
259 | */
260 | public void setResults(Map results) {
261 | this.results = results;
262 | }
263 | public String getCaseId() {
264 | return caseId;
265 | }
266 | public void setCaseId(String caseId) {
267 | this.caseId = caseId;
268 | }
269 | public String getTestPoint() {
270 | return testPoint;
271 | }
272 | public void setTestPoint(String testPoint) {
273 | this.testPoint = testPoint;
274 | }
275 |
276 | }
277 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/autocreate/CreateJavaFile.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase.autocreate;
2 |
3 | import java.util.Map;
4 | import java.util.TreeMap;
5 |
6 | import com.github.qihootest.leo.ift.IftConf;
7 | import com.github.qihootest.leo.toolkit.util.FreeMakerUtil;
8 | import com.github.qihootest.leo.toolkit.util.LogUtil;
9 |
10 |
11 | /**
12 | * 封装用例集,对集合进行分组格式化等处理,当前是1个excel文件对应1个此类的实例
13 | * @author lianghui (lianghui@360.cn) 2012-11-12
14 | *
15 | */
16 | public class CreateJavaFile {
17 |
18 | private static LogUtil log=LogUtil.getLogger(CreateJavaFile.class);//日志记录
19 |
20 | /**
21 | * 根据用例数据信息创建对应的.java源文件
22 | * @return boolean 创建成功返回true,失败返回false
23 | */
24 | public boolean creatJavaSrcFile(JavaCaseInfo javaInfo) {
25 | String javaFilePathTmp = javaInfo.getJavaSavePath()+javaInfo.getJavaFileName()+".java";
26 | FreeMakerUtil creatjava=new FreeMakerUtil();
27 | if (creatjava.CreateJavaFile(IftConf.TemplatePath, getJavaFileData(javaInfo), javaFilePathTmp)) {
28 | log.info("创建"+javaInfo.getJavaFileName()+"对应的.java文件成功:"+javaFilePathTmp);
29 | return true;
30 | }
31 | log.error("创建"+javaInfo.getJavaFileName()+"对应的.java文件成功失败:"+javaFilePathTmp);
32 | return false;
33 | }
34 |
35 |
36 | //私有方法
37 | private Map getJavaFileData(JavaCaseInfo javaInfo){
38 | Map data = new TreeMap();
39 | data.put("javaInfo", javaInfo);
40 | Map clsInfo = new TreeMap<>();
41 | String impotInfo = javaInfo.getCls().getPackage().toString();
42 | impotInfo = impotInfo.substring(8)+"."+javaInfo.getCls().getSimpleName();
43 | clsInfo.put("importInfo", impotInfo);
44 | clsInfo.put("className", javaInfo.getCls().getSimpleName());
45 | clsInfo.put("method", javaInfo.getMethod());
46 | data.put("clsInfo", clsInfo);
47 | return data;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/autocreate/IftDataFileCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase.autocreate;
2 |
3 | import java.util.List;
4 |
5 | import com.github.qihootest.leo.dispatch.testcase.CreateXmlFile;
6 | import com.github.qihootest.leo.dispatch.testcase.SuperCase;
7 | import com.github.qihootest.leo.ift.IftConf;
8 | import com.github.qihootest.leo.ift.testcase.format.FormatCase;
9 | import com.github.qihootest.leo.toolkit.util.CompilerUtil;
10 | import com.github.qihootest.leo.toolkit.util.FileUtil;
11 | import com.github.qihootest.leo.toolkit.util.LogUtil;
12 | import com.github.qihootest.leo.toolkit.util.StringUtil;
13 |
14 |
15 | /**
16 | * 数据文件类型的测试用例
17 | * @author lianghui (lianghui@360.cn)
18 | *
19 | */
20 | public class IftDataFileCase extends SuperCase{
21 |
22 | private LogUtil log =LogUtil.getLogger(IftDataFileCase.class);//日志记录
23 | //任务名称
24 | private String taskName;//测试任务名称
25 | //根据用例数据文件,创建java、xml文件相关配置信息
26 | private String allReportPath ;// html、excel测试报告存储的上级目录
27 | private String excelReportName;//excel格式测试报告的名称
28 |
29 | private CreateJavaFile createJavaFile;//生成java文件
30 | private CreateXmlFile createXmlFile;//生成xml文件
31 |
32 | /**
33 | * 构造函数
34 | */
35 | public IftDataFileCase() {
36 | super();
37 | createJavaFile = new CreateJavaFile();
38 | createXmlFile = new CreateXmlFile();
39 | setIftTaskName("未命名测试任务");
40 | excelReportName="未命名接口测试";
41 | }
42 |
43 | /**
44 | * 构造函数
45 | * @param threadCont 线程数
46 | * @param setParallel 设置多线程执行方式methods;tests;classes
47 | */
48 | public IftDataFileCase(String setParallel,int threadCont) {
49 | super();
50 | if(setParallel.equals("methods")||setParallel.equals("tests")||setParallel.equals("classes")){
51 | createJavaFile = new CreateJavaFile();
52 | createXmlFile = new CreateXmlFile(threadCont,setParallel);
53 | setIftTaskName("未命名测试任务");
54 | excelReportName="未命名接口测试";
55 | }else{
56 | log.error("多线程不支持:"+setParallel+"方法");
57 | }
58 |
59 |
60 | }
61 |
62 | public List getCaseList() {
63 | createXmlFile();
64 | return this.xmlPathNameList;
65 | }
66 | /**
67 | * 添加用例
68 | * @param casePath 用例路径 必填
69 | * @param sheetName Excel的sheet表名 可选
70 | * @param caseName 用例名称 必填
71 | * @param cls 执行用例的类 必填
72 | * @param method 类中的方法 必填
73 | */
74 | public void addCase(String casePath, String sheetName, String caseName,
75 | Class> cls,String method) {
76 | if (StringUtil.IsNullOrEmpty(casePath) || StringUtil.IsNullOrEmpty(caseName)) {
77 | return;
78 | }//任一项空值或长度小于1时,不做处理
79 | JavaCaseInfo javaCaseInfo = new JavaCaseInfo();
80 | //读取用例
81 | FormatCase formatcase=new FormatCase();
82 | formatcase.FormatCaseFromObj(casePath,sheetName);
83 | //存储用例实体列表信息
84 | javaCaseInfo.setAllCase(formatcase.getTestCase());
85 | //获取测试集名称作为输出的测试报告名称
86 | this.excelReportName="测试报告_"+formatcase.getCasesetName();
87 | //存储javaCaseInfo其余信息
88 | javaCaseInfo.setPackageName(IftConf.PackageName);
89 | javaCaseInfo.setJavaFileName(caseName.replace(".", "_"));
90 | javaCaseInfo.setJavaSavePath(IftConf.JavaPath);
91 | javaCaseInfo.setCaseDataPathName(casePath);
92 | javaCaseInfo.setCaseDataSheetName(sheetName);
93 | javaCaseInfo.setExcelReportSheetName(sheetName);
94 | javaCaseInfo.setExcelReportName(this.excelReportName);
95 | javaCaseInfo.setExcelReportPath(getReportPath());
96 | javaCaseInfo.setCls(cls);
97 | javaCaseInfo.setMethod(method);
98 | //创建java文件 失败则返回
99 | if (!createJavaFile.creatJavaSrcFile(javaCaseInfo)){
100 | return;
101 | }
102 | //编译java文件为class 失败则返回
103 | if(!CompilerUtil.dynamicCompiler(javaCaseInfo.getJavaSavePath()+javaCaseInfo.getJavaFileName()+".java",
104 | IftConf.DistPath, IftConf.LibPath,IftConf.JarFile)){
105 | return;
106 | }
107 | //添加到xmlSuite
108 | try {
109 | createXmlFile.addJavaCase(caseName.replace(".", "_"),
110 | Class.forName(javaCaseInfo.getPackageName()+"."+javaCaseInfo.getJavaFileName()));
111 | log.info("添加测试集:"+javaCaseInfo.getJavaFileName()+"成功");
112 | } catch (ClassNotFoundException e) {
113 | log.error("添加测试集:"+javaCaseInfo.getJavaFileName()+"失败");
114 | log.error(e.getMessage());
115 | }
116 |
117 | }
118 |
119 | /**
120 | * 添加用例
121 | * @param casePath 用例路径 必填
122 | * @param caseName 用例名称 必填
123 | * @param cls 执行用例的类 必填
124 | * @param method 类中的方法 必填
125 | */
126 | public void addCase(String casePath, String caseName,Class> cls,String method) {
127 | addCase(casePath,"TestCase",caseName,cls,method);
128 | }
129 |
130 | /**
131 | * 设置任务名称
132 | * @param setTaskName
133 | */
134 | public void setIftTaskName(String setTaskName) {
135 | this.taskName =setTaskName;
136 | this.createXmlFile.setSuiteName(this.taskName);
137 | setReportPath(IftConf.ReportPath+this.taskName+"/");// 测试报告存储路径
138 | this.excelReportName = this.taskName;
139 |
140 | }
141 |
142 |
143 | /**
144 | * 如果路径无效 则测试报告默认保存在 [qtaf/ift/report/任务名称 ]目录下
145 | * @param reportPath
146 | */
147 | public void setReportPath(String reportPath){
148 | if (FileUtil.createDictory(reportPath)) {
149 | this.allReportPath=reportPath;
150 | //清空指定的测试报告目录
151 | FileUtil.delFolder(getReportPath());
152 | }
153 | }
154 | public String getReportPath(){
155 | return this.allReportPath;
156 | }
157 | public String getExcelReportPath() {
158 | return getReportPath()+this.excelReportName+".xlsx";
159 | }
160 | public String getHtmlReportPath() {
161 | return this.allReportPath+"html";
162 | }
163 |
164 | public String getTaskName() {
165 | return taskName;
166 | }
167 |
168 | /**
169 | * 创建java、xml文件 更新xmlPathNameList列表
170 | * @return boolean 设置成功返回true
171 | */
172 | private boolean createXmlFile() {
173 | //创建java、xml文件
174 | String xmlFilePath = createXmlFile.getXmlFilePath();
175 | if (FileUtil.isExist(xmlFilePath)) {
176 | xmlPathNameList.add(xmlFilePath);
177 | log.info("添加xml文件成功:"+xmlFilePath);
178 | return true;
179 | }else{
180 | log.error("添加xml文件失败:"+xmlFilePath);
181 | return false;
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/autocreate/JavaCaseInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase.autocreate;
2 |
3 | import java.util.List;
4 |
5 | import com.github.qihootest.leo.ift.testcase.IftTestCase;
6 |
7 | /**
8 | * 生成java文件时需要的信息
9 | * @author lianghui (lianghui@360.cn)
10 | *
11 | */
12 | public class JavaCaseInfo {
13 |
14 | private List allCase;//所有用例列表
15 | private String packageName;//包名
16 | private String javaFileName;//java文件名
17 | private String javaSavePath;//java文件保存目录
18 | private String caseDataPathName;//用例数据文件路径
19 | private String caseDataSheetName;//Excel用例数据sheet名称
20 |
21 | private Class> cls;//执行用例的类名
22 | private String method;//执行用例的方法名
23 |
24 | private String excelReportSheetName;//excel测试报告sheet名称
25 | private String excelReportName;//excel测试报告文件名称
26 | private String excelReportPath;//excel测试报告存储目录
27 | public List getAllCase() {
28 | return allCase;
29 | }
30 | public void setAllCase(List allCase) {
31 | this.allCase = allCase;
32 | }
33 | public String getPackageName() {
34 | return packageName;
35 | }
36 | public void setPackageName(String packageName) {
37 | this.packageName = packageName;
38 | }
39 | public String getJavaFileName() {
40 | return javaFileName;
41 | }
42 | public void setJavaFileName(String javaFileName) {
43 | this.javaFileName = javaFileName;
44 | }
45 | public String getJavaSavePath() {
46 | return javaSavePath;
47 | }
48 | public void setJavaSavePath(String javaSavePath) {
49 | this.javaSavePath = javaSavePath;
50 | }
51 | public String getCaseDataPathName() {
52 | return caseDataPathName;
53 | }
54 | public void setCaseDataPathName(String caseDataPathName) {
55 | this.caseDataPathName = caseDataPathName;
56 | }
57 | public String getExcelReportSheetName() {
58 | return excelReportSheetName;
59 | }
60 | public void setExcelReportSheetName(String excelReportSheetName) {
61 | this.excelReportSheetName = excelReportSheetName;
62 | }
63 | public String getExcelReportName() {
64 | return excelReportName;
65 | }
66 | public void setExcelReportName(String excelReportName) {
67 | this.excelReportName = excelReportName;
68 | }
69 | public String getExcelReportPath() {
70 | return excelReportPath;
71 | }
72 | public void setExcelReportPath(String excelReportPath) {
73 | this.excelReportPath = excelReportPath;
74 | }
75 | public String getCaseDataSheetName() {
76 | return caseDataSheetName;
77 | }
78 | public void setCaseDataSheetName(String caseDataSheetName) {
79 | this.caseDataSheetName = caseDataSheetName;
80 | }
81 | public Class> getCls() {
82 | return cls;
83 | }
84 | public void setCls(Class> cls) {
85 | this.cls = cls;
86 | }
87 | public String getMethod() {
88 | return method;
89 | }
90 | public void setMethod(String method) {
91 | this.method = method;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/format/FormatCase.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase.format;
2 |
3 | import java.util.ArrayList;
4 | import java.util.LinkedHashMap;
5 | import java.util.List;
6 |
7 | import com.github.qihootest.leo.ift.IftConf;
8 | import com.github.qihootest.leo.ift.testcase.IftTestCase;
9 | import com.github.qihootest.leo.toolkit.util.FileUtil;
10 | import com.github.qihootest.leo.toolkit.util.LogUtil;
11 | import com.github.qihootest.leo.toolkit.util.StringUtil;
12 |
13 | /**
14 | * 功能说明:格式化测试用例数据, 目前只支持读取excel格式数据,XML、数据库等后续扩展
15 | * @author lianghui (lianghui@360.cn)
16 | */
17 | public class FormatCase {
18 |
19 | private static LogUtil log=LogUtil.getLogger(FormatCase.class);//日志记录
20 |
21 | private List> arrCase;
22 | private List testCase;
23 | private String url;
24 | private String httpMethod;
25 | private int argcount;
26 | private String cookie;
27 | private String casesetName;
28 | private String sheetName;
29 |
30 | /**
31 | * 构造函数
32 | */
33 | public FormatCase() {
34 | this.arrCase = new ArrayList>();
35 | this.testCase = new ArrayList();
36 | this.url = "";
37 | this.httpMethod = "";
38 | this.argcount = 0;
39 | this.cookie = "";
40 | this.casesetName = "";
41 | this.sheetName = "";
42 | }
43 |
44 | /**
45 | * 从数据文件中读取测试用例
46 | * @param casePath
47 | * @param sheetName
48 | */
49 | public void FormatCaseFromObj(String casePath,String sheetName) {
50 | this.FormatCaseFromExcel(casePath,sheetName);
51 | this.casesetName = casePath.substring(casePath.lastIndexOf("/") + 1,casePath.length() - (FileUtil.getExtensionName(casePath).length()+1));
52 | this.sheetName=sheetName;
53 | }
54 |
55 | /**
56 | *
57 | * @param pathName
58 | * @param sheetIndex
59 | */
60 | private void getTestCaseFromExcel(String pathName,String sheetName) {
61 | // 记录读取到的用例数据信息
62 | ReadCaseFromExcel readCase = new ReadCaseFromExcel(pathName,sheetName);
63 | // 读取表表信息
64 | url = readCase.readUrl();
65 | httpMethod = readCase.readHttpMethod();
66 | argcount = readCase.readArgCount();// 参与sign计算的参数个数
67 | this.cookie = readCase.readCookie();
68 | String[] argKey = readCase.readArgKey();// 获取参数key
69 | // 设置测试集名称,Excel中未存储,从判断传入的path为excel时设置
70 | // casesetName=readcase();
71 | // 获取用例数据
72 | for (int j = IftConf.paramStartRow; j < readCase.getRowNum(); j++) {
73 | IftTestCase tempcase = new IftTestCase();
74 | tempcase.setArgCount(getArgcount());
75 | tempcase.setHttpMethod(getHttpMethod());
76 | tempcase.setUrl(getUrl());
77 | tempcase.setCookie(this.cookie);
78 | // 读取【j+1】行的数据
79 | String[] argValue = readCase.readArgValue(j);
80 | // System.out.print("行:"+j+" 参数个数"+argValue.length+argValue[1]+"\n");
81 | if (!argValue[IftConf.isRunCol].equals("Y")) {
82 | continue;
83 | } else {
84 | // 把数据和标题一一对应存入LinkedHashMap有序键值对中
85 | LinkedHashMap mycase = new LinkedHashMap();
86 | for (int i = 0; i < argKey.length; i++) {
87 | // 判断此条记录是否有cookie参数,如果有且不为空替换全局cookie
88 | if (null != argValue[i] && argKey[i].equals("cookie") && argValue[i].length() > 0) {
89 | String dependCookie = (String) (IftConf.DependPara).get("cookie"); //重新赋值value
90 | tempcase.setCookie(StringUtil.paramReplace(argValue[i], dependCookie)); //cookie参数依赖(将之前接口获取的cookie值进行替换)
91 | }
92 | mycase.put(argKey[i], argValue[i]);
93 | }
94 | arrCase.add(mycase);
95 | tempcase.setCaseMap(mycase);
96 | tempcase.setCaseId(mycase.get("CaseID"));
97 | tempcase.setTestPoint(mycase.get("TestPoint"));
98 | }
99 | this.testCase.add(tempcase);
100 | }
101 | }
102 |
103 | /**
104 | * 功能:格式化Excel格式的测试用例
105 | *
106 | * @param casefilepath 读取Sheet1表格
107 | */
108 | public void FormatCaseFromExcel(String casefilepath) {
109 | // 记录读取到的用例数据信息
110 | getTestCaseFromExcel(casefilepath, "Sheet1");
111 |
112 | }
113 |
114 | /**
115 | * 功能:格式化Excel格式的测试用例
116 | *
117 | * @param casefilepath
118 | * @param casesheetname 如果为空,默认读取Sheet1
119 | */
120 | public void FormatCaseFromExcel(String casefilepath, String casesheetname) {
121 | if (null==casesheetname | casesheetname.length()<1) {
122 | casesheetname="Sheet1";
123 | }
124 | getTestCaseFromExcel(casefilepath, casesheetname);
125 | }
126 |
127 | /**
128 | * 功能:格式化Xml格式的测试用例
129 | *
130 | * @param casefilepath
131 | */
132 | public void FormatCaseFromXml(String casefilepath) {
133 |
134 | }
135 |
136 | /**
137 | * @return the arrCase
138 | */
139 | public List> getArrCase() {
140 | return arrCase;
141 | }
142 |
143 | /**
144 | * @return the testCase
145 | */
146 | public List getTestCase() {
147 | return testCase;
148 | }
149 |
150 | /**
151 | * @return the url
152 | */
153 | public String getUrl() {
154 | return url;
155 | }
156 |
157 | /**
158 | * @return the httpMethod
159 | */
160 | public String getHttpMethod() {
161 | return httpMethod;
162 | }
163 |
164 | /**
165 | * @return the argcount
166 | */
167 | public int getArgcount() {
168 | return argcount;
169 | }
170 |
171 | /**
172 | * @return the cookie
173 | */
174 | public String getCookie() {
175 | return cookie;
176 | }
177 |
178 | /**
179 | * @return the casesetName
180 | */
181 | public String getCasesetName() {
182 | return casesetName;
183 | }
184 |
185 | public String getSheetName() {
186 | return sheetName;
187 | }
188 |
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/testcase/format/ReadCaseFromExcel.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.testcase.format;
2 | import java.util.List;
3 | import com.github.qihootest.leo.ift.IftConf;
4 | import com.github.qihootest.leo.toolkit.util.ExcelUtil;
5 |
6 | /**
7 | * 功能说明:从excel表中读取用例数据文件
8 | * @author lianghui (lianghui@360.cn)
9 | */
10 | public class ReadCaseFromExcel {
11 | private ExcelUtil excel;
12 | private int rowNum;
13 | private String url;
14 | private String httpMethod;
15 | private String cookie;
16 | private int argcount;
17 | private String[] argKey;
18 | private String[] argValue;
19 |
20 | /**
21 | * 构造函数
22 | * @param pathName
23 | * @param sheetName
24 | */
25 | public ReadCaseFromExcel(String pathName,String sheetName){
26 | excel=new ExcelUtil(pathName, sheetName);
27 | }
28 | /**
29 | * 构造函数
30 | * @param pathName
31 | */
32 | public ReadCaseFromExcel(String pathName){
33 | excel=new ExcelUtil(pathName, 0);
34 | }
35 |
36 | /**
37 | * 功能说明:从excel表格B1中读取url并返回
38 | * @return String
39 | */
40 | public String readUrl(){
41 | this.url=excel.getCellValue(IftConf.urlRow, IftConf.urlCol).trim();
42 | return this.url;
43 | }
44 |
45 | /**
46 | * 功能说明:从excel表格B2中读取readHttpMethod并返回
47 | * @return String
48 | */
49 | public String readHttpMethod(){
50 | this.httpMethod = excel.getCellValue(IftConf.methodRow, IftConf.methodCol).trim();
51 | return this.httpMethod;
52 | }
53 |
54 | /**
55 | * 功能说明:从excel表格B3中读取Cookie并返回
56 | *
57 | * @return String
58 | *
59 | */
60 | public String readCookie(){
61 | this.cookie = excel.getCellValue(IftConf.cookieRow, IftConf.cookieCol).trim();
62 | return this.cookie;
63 | }
64 |
65 | /**
66 | * 功能说明:从excel表格B4中读取argcount并返回
67 | *
68 | * @return int
69 | *
70 | */
71 | public int readArgCount(){
72 | this.argcount = Integer.parseInt(excel.getCellValue(IftConf.argCountRow, IftConf.argCountCol).trim());
73 | return this.argcount;
74 | }
75 |
76 | /**
77 | * 功能说明:从excel表格第5行中读取参数标题并返回
78 | *
79 | * @return String[]
80 | *
81 | */
82 | public String[] readArgKey(){
83 | this.argKey=new String[excel.getColNum()];
84 | Listkeylist=excel.getRowList(IftConf.typeRow);
85 | for (int i = 0; i < keylist.size(); i++) {
86 | argKey[i]=keylist.get(i);
87 | }
88 | return this.argKey;
89 | }
90 |
91 | /**
92 | * 功能说明:从excel表格指定行开始读取所有值并返回
93 | *
94 | * @param argKeynum
95 | * @return String[]
96 | *
97 | */
98 | public String[] readArgValue(int argKeynum){
99 | this.argValue=new String[excel.getColNum()];
100 | Listvaluelist=excel.getRowList(argKeynum);
101 | for (int i = 0; i < valuelist.size(); i++) {
102 | argValue[i]=valuelist.get(i);
103 | }
104 | return this.argValue;
105 | }
106 |
107 | /**
108 | * Excel的行数
109 | * @return int
110 | */
111 | public int getRowNum() {
112 | this.rowNum=excel.getRowNum();
113 | return this.rowNum;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/util/CommonSign.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.util;
2 |
3 | import java.util.Iterator;
4 | import java.util.Map.Entry;
5 | import java.util.TreeMap;
6 | import com.github.qihootest.leo.toolkit.util.CommUtils;
7 | import com.github.qihootest.leo.toolkit.util.LogUtil;
8 |
9 | /**
10 | * 各种签名算法
11 | * @author @bwgang (bwgang@163.com)
12 | */
13 | public class CommonSign {
14 |
15 | private static LogUtil log = LogUtil.getLogger(CommonSign.class);// 日志记录
16 |
17 | /**
18 | * 说明:签名算法1 key=value&key=value&.....key=valuesecret_key 最后连接私钥时不带&符号
19 | * 计算MD5时,中文按照UTF-8编码计算
20 | * @param signpara 参与签名计算的键值对
21 | * @param secret_key 签名计算所需的密钥
22 | * @return String
23 | */
24 | public static String signMethodOne(TreeMap signpara,
25 | String secret_key) {
26 | String expBaseSign = "";
27 | String expSign = "";
28 |
29 | Iterator> ite = signpara.entrySet().iterator();
30 | while (ite.hasNext()) {
31 | Entry, ?> entry = (Entry, ?>) ite.next();
32 | expBaseSign = expBaseSign + entry.getKey() + "="
33 | + entry.getValue().toString() + "&";
34 | }
35 | if (expBaseSign.length() < 1) {
36 | expSign = expBaseSign + secret_key;
37 | } else {
38 | expSign = expBaseSign.substring(0, expBaseSign.length() - 1)
39 | + secret_key;
40 | }
41 | return CommUtils.getMD5(expSign,"UTF-8");
42 | }
43 |
44 | /**
45 | * 说明:签名算法2 key=value&key=value&.....key=valuesecret_key 最后连接私钥时不带&符号
46 | * 计算MD5时,中文按照GBK编码计算
47 | * @param signpara 参与签名计算的键值对
48 | * @param secret_key 签名计算所需的密钥
49 | * @return String
50 | */
51 | public static String signMethodTwo(TreeMap signpara,String secret_key) {
52 | String expBaseSign = "";
53 | String expSign = "";
54 |
55 | Iterator> ite = signpara.entrySet().iterator();
56 | while (ite.hasNext()) {
57 | Entry, ?> entry = (Entry, ?>) ite.next();
58 | expBaseSign = expBaseSign + entry.getKey() + "="
59 | + entry.getValue().toString() + "&";
60 | }
61 | if (expBaseSign.length() < 1) {
62 | expSign = expBaseSign + secret_key;
63 | } else {
64 | expSign = expBaseSign.substring(0, expBaseSign.length() - 1)
65 | + secret_key;
66 | }
67 | log.info("签名计算串:" + expSign);
68 | return CommUtils.getMD5Gbk(expSign);
69 | }
70 |
71 | /**
72 | * 说明:签名算法3 value为空时 不参与签名计算 key=value&key=value&.....key=valuesecret_key
73 | * 最后连接私钥时不带&符号 计算MD5时,中文按照UTF-8编码计算
74 | *
75 | * @param signpara 参与签名计算的键值对
76 | * @param secret_key 签名计算所需的密钥
77 | * @return String
78 | */
79 | public static String signMethodThird(TreeMap signpara,
80 | String secret_key) {
81 | String expBaseSign = "";
82 | String expSign = "";
83 |
84 | Iterator> ite = signpara.entrySet().iterator();
85 | while (ite.hasNext()) {
86 | Entry, ?> entry = (Entry, ?>) ite.next();
87 | if (entry.getValue().toString().length() > 0) {
88 | expBaseSign = expBaseSign + entry.getKey() + "=" + entry.getValue().toString() + "&";
89 | }
90 | }
91 | if (expBaseSign.length() < 1) {
92 | expSign = expBaseSign + secret_key;
93 | } else {
94 | expSign = expBaseSign.substring(0, expBaseSign.length() - 1) + secret_key;
95 | }
96 | return CommUtils.getMD5(expSign,"UTF-8");
97 | }
98 |
99 | /**
100 | * 说明:签名算法4 value为空时 不参与签名计算 key=value&key=value&.....key=valuesecret_key
101 | * 最后连接私钥时带&符号 计算MD5时,中文按照UTF-8编码计算
102 | *
103 | * @param signpara 参与签名计算的键值对
104 | * @param secret_key 签名计算所需的密钥
105 | * @return String
106 | */
107 | public static String signMethodFour(TreeMap signpara,
108 | String secret_key) {
109 | String expBaseSign = "";
110 | String expSign = "";
111 |
112 | Iterator> ite = signpara.entrySet().iterator();
113 | while (ite.hasNext()) {
114 | Entry, ?> entry = (Entry, ?>) ite.next();
115 | if (entry.getValue().toString().length() > 0) {
116 | String value=entry.getValue().toString();
117 | // if (MyString.isChinese(value)) {
118 | // value=CommUtils.urlEncode(value, IEnCoding.UTF8);
119 | // }
120 | expBaseSign = expBaseSign + entry.getKey() + "=" +value + "&";
121 | }
122 | }
123 | if (expBaseSign.length() < 1) {
124 | expSign = expBaseSign + secret_key;
125 | } else {
126 | expSign = expBaseSign.substring(0, expBaseSign.length() - 1) + secret_key;
127 | }
128 | // log.info("参与签名的串:"+expSign);
129 | // log.info("计算后的结果为:"+CommUtils.getMD5(expSign,IEnCoding.UTF8));
130 | return CommUtils.getMD5(expSign,"UTF-8");
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/ift/util/ExportReportExcel.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.ift.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.LinkedHashMap;
5 | import java.util.LinkedList;
6 | import java.util.List;
7 |
8 | import org.apache.poi.hssf.util.HSSFColor;
9 | import org.apache.poi.ss.usermodel.CellStyle;
10 | import org.apache.poi.ss.usermodel.IndexedColors;
11 | import org.apache.poi.xssf.usermodel.XSSFCell;
12 | import org.apache.poi.xssf.usermodel.XSSFFont;
13 | import org.apache.poi.xssf.usermodel.XSSFRichTextString;
14 | import org.apache.poi.xssf.usermodel.XSSFRow;
15 | import org.apache.poi.xssf.usermodel.XSSFSheet;
16 | import org.apache.poi.xssf.usermodel.XSSFWorkbook;
17 |
18 |
19 | import com.github.qihootest.leo.toolkit.util.CommUtils;
20 | import com.github.qihootest.leo.toolkit.util.ExcelUtil;
21 | import com.github.qihootest.leo.toolkit.util.FileUtil;
22 |
23 | /**
24 | * 说明:输出Excel格式的测试报告
25 | * @author @bwgang (bwgang@163.com)
26 | */
27 | public class ExportReportExcel {
28 |
29 | // private static LogUtil log = LogUtil.getLogger(ExportReportExcel.class);
30 | /**
31 | * 根据测试执行过程中的记录,生成Excel的测试报告
32 | * @param excelreportpath
33 | * @param filename
34 | * @param arrres
35 | * @return boolean
36 | */
37 | public boolean CreatReportExcel(String excelreportpath,String filename,ArrayList> arrres) {
38 | boolean flag=false;
39 | flag=CreatReportExcel(excelreportpath,filename,null,arrres);
40 | return flag;
41 | }
42 |
43 | /**
44 | * 根据测试执行过程中的记录,生成Excel的测试报告
45 | * @param excelreportpath
46 | * @param filename
47 | * @param sheetname
48 | * @param arrres
49 | * @return boolean
50 | */
51 | public boolean CreatReportExcel(String excelreportpath,String filename,String sheetname,List> arrres) {
52 | boolean flag=false;
53 | List title =null;
54 | List content=null;
55 |
56 | if (filename.indexOf("/")!=-1 || filename.indexOf("xls")!=-1 || filename.length()<1) {
57 | filename="未命名";
58 | }
59 |
60 | title=getTitle();
61 | content=getContentFromMap(arrres);
62 | writeExcel(title, content, sheetname,filename,excelreportpath);
63 |
64 | return flag;
65 | }
66 |
67 | //excel测试报告内容定义
68 |
69 | /**
70 | * 说明:指定输出报告的标题项,按顺序存储到List中
71 | *
72 | * @return List title
73 | */
74 | private List getTitle() {
75 | List title=new LinkedList() ;
76 |
77 | title.add("CaseID");
78 | title.add("TestPoint");
79 | title.add("ExpRes");
80 | title.add("ActRes");
81 | title.add("ExcResult");
82 | title.add("ResponseRes");
83 | title.add("Httpurl");
84 |
85 | return title;
86 | }
87 |
88 | /**
89 | * 说明:解析出内容,按顺序存储到List中,每行为一字符数组
90 | * @param arrres
91 | * @return List
92 | */
93 | private List getContentFromMap(List> arrres) {
94 | List content=new LinkedList();
95 |
96 | List title=getTitle();
97 |
98 |
99 | for (int i = 0; i < arrres.size(); i++) {
100 | String[] str = new String[title.size()];
101 | for (int j = 0; j < title.size(); j++) {
102 | str[j]=arrres.get(i).get(title.get(j));
103 | }
104 | content.add(str);
105 | }
106 |
107 | return content;
108 | }
109 |
110 | //Excel报告格式定义
111 | private void writeExcel(List title,List datas,String sheetName,String excelName,String excelreportpath){
112 | // log.error("title:");
113 | // for (int i = 0; i < title.size(); i++) {
114 | // log.error(title.get(i));
115 | // }
116 | // log.error("datas:");
117 | // for (int i = 0; i < datas.size(); i++) {
118 | // log.error("data--"+i);
119 | // for (String str : datas.get(i)) {
120 | // log.error(str);
121 | // }
122 | //
123 | // }
124 | // log.error("sheetname--"+sheetName);
125 | // log.error("excelName--"+excelName);
126 | // log.error("excelreportpath--"+excelreportpath);
127 | ExcelUtil excel = new ExcelUtil();
128 | String pathName=excelreportpath+"/"+excelName+".xlsx";
129 |
130 | if (!FileUtil.isExist(pathName)) {//不存在时,新建空白工作簿
131 | excel.createBlankExcel2007(pathName);
132 | }
133 |
134 | //读取Excel文件
135 | excel.setPathName(pathName);
136 | XSSFWorkbook wb=(XSSFWorkbook) excel.getWb();
137 | updateWbFromContent(wb,title,datas,sheetName);//测试结果写入工作簿
138 | excel.writeExcel2007(wb, pathName);//写入文件
139 | }
140 |
141 | private void updateWbFromContent(XSSFWorkbook wb,List title, List datas,
142 | String sheetName) {
143 |
144 | XSSFSheet sheet;
145 | if (null==sheetName | sheetName.length()<1) {//未指定sheet名称时,自动命名创建
146 | sheet=wb.createSheet();
147 | }else{
148 | if (null!=wb.getSheet(sheetName)) sheetName=sheetName+CommUtils.getRandomStr(5);//已存在同名sheet,添加随机字符串
149 | sheet=wb.createSheet(sheetName);//使用指定的sheetName
150 | }
151 |
152 | //设置列宽
153 | sheet.setColumnWidth(0, 15*255);
154 | sheet.setColumnWidth(1, 25*255);
155 | sheet.setColumnWidth(2, 40*255);
156 | sheet.setColumnWidth(3, 40*255);
157 | sheet.setColumnWidth(4, 10*255);
158 | sheet.setColumnWidth(5, 50*255);
159 | sheet.setColumnWidth(6, 15*255);
160 | sheet.setColumnWidth(7, 20*255);
161 | sheet.setColumnWidth(8, 10*255);
162 | //创建第一行
163 | XSSFRow row=sheet.createRow(0);
164 | //表头样式
165 | XSSFFont fonttitle=wb.createFont();
166 | fonttitle.setItalic(true);
167 | fonttitle.setFontName("微软雅黑");
168 | fonttitle.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗体
169 |
170 | CellStyle styletitle = wb.createCellStyle();
171 | styletitle.setFillForegroundColor(IndexedColors.BRIGHT_GREEN.getIndex());
172 | styletitle.setFillPattern(CellStyle.SOLID_FOREGROUND);
173 | styletitle.setFont(fonttitle);
174 | styletitle.setBorderLeft(CellStyle.BORDER_THIN);
175 | styletitle.setBorderRight(CellStyle.BORDER_THIN);
176 | styletitle.setBorderTop(CellStyle.BORDER_THIN);
177 | styletitle.setBorderBottom(CellStyle.BORDER_THIN);
178 |
179 | //正文样式
180 | //
181 | XSSFFont fontRed=wb.createFont();
182 | fontRed.setColor(HSSFColor.GREEN.index);
183 | fontRed.setColor(IndexedColors.RED.getIndex());
184 | fontRed.setFontName("微软雅黑");
185 | fontRed.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗体
186 | fontRed.setFontHeightInPoints((short) 11);
187 | CellStyle styleRed = wb.createCellStyle();
188 | styleRed.setFont(fontRed);
189 | styleRed.setBorderLeft(CellStyle.BORDER_THIN);
190 | styleRed.setBorderRight(CellStyle.BORDER_THIN);
191 | styleRed.setBorderTop(CellStyle.BORDER_THIN);
192 | styleRed.setBorderBottom(CellStyle.BORDER_THIN);
193 |
194 | XSSFFont fontGreen=wb.createFont();
195 | fontGreen.setColor(IndexedColors.GREEN.getIndex());
196 | fontGreen.setFontName("微软雅黑");
197 | fontGreen.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗体
198 | fontGreen.setFontHeightInPoints((short) 11);
199 | CellStyle styleGreen = wb.createCellStyle();
200 | styleGreen.setFont(fontGreen);
201 | styleGreen.setBorderLeft(CellStyle.BORDER_THIN);
202 | styleGreen.setBorderRight(CellStyle.BORDER_THIN);
203 | styleGreen.setBorderTop(CellStyle.BORDER_THIN);
204 | styleGreen.setBorderBottom(CellStyle.BORDER_THIN);
205 |
206 | XSSFFont fontBlack=wb.createFont();
207 | fontBlack.setColor(IndexedColors.BLACK.getIndex());
208 | fontBlack.setFontName("Arial");
209 | fontBlack.setFontHeightInPoints((short) 9);
210 | CellStyle styleBlack = wb.createCellStyle();
211 | styleBlack.setFont(fontBlack);
212 | styleBlack.setBorderLeft(CellStyle.BORDER_THIN);
213 | styleBlack.setBorderRight(CellStyle.BORDER_THIN);
214 | styleBlack.setBorderTop(CellStyle.BORDER_THIN);
215 | styleBlack.setBorderBottom(CellStyle.BORDER_THIN);
216 | //写入标题
217 | for (int i = 0; i < title.size(); i++) {
218 | XSSFCell cell=row.createCell(i, XSSFCell.CELL_TYPE_STRING);
219 | cell.setCellValue(title.get(i));
220 | cell.setCellStyle(styletitle);
221 | }
222 |
223 | // 写入内容行
224 | for (int i = 0; i < datas.size(); i++) {
225 | String[] rowvalue = datas.get(i);
226 | XSSFRow nextrow = sheet.createRow(sheet.getLastRowNum() + 1);// 创建一行
227 | for (int j = 0; j < rowvalue.length; j++) {
228 | XSSFCell cell = nextrow
229 | .createCell(j, XSSFCell.CELL_TYPE_STRING);
230 | cell.setCellValue(new XSSFRichTextString(rowvalue[j]));
231 | if (rowvalue[j].equals("Fail")) {
232 | cell.setCellStyle(styleRed);
233 | } else if (rowvalue[j].equals("Pass")) {
234 | cell.setCellStyle(styleGreen);
235 | } else {
236 | cell.setCellStyle(styleBlack);
237 | }
238 |
239 | }
240 | }
241 |
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/QtafException.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit;
2 |
3 | /**
4 | * 自定义异常信息
5 | * @author @bwgang (bwgang@163.com)
6 | *
7 | */
8 | public class QtafException extends RuntimeException {
9 |
10 | private static final long serialVersionUID = 1L;
11 |
12 | public QtafException() {
13 | }
14 |
15 | public QtafException(String message) {
16 | super(message);
17 | }
18 |
19 | public QtafException(String message, Throwable cause) {
20 | super(message, cause);
21 | }
22 |
23 | public QtafException(Throwable cause) {
24 | super(cause);
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/TkConf.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 |
9 |
10 | /**
11 | * 配置文件
12 | * @author @bwgang (bwgang@163.com)
13 | */
14 | public class TkConf {
15 |
16 | /**
17 | * 根目录
18 | */
19 | public static String RootPath = getRootPath();
20 | /**
21 | * log4j配置文件目录
22 | */
23 | public static String Log4jConf = RootPath+"leo/toolkit/log4j.properties";
24 |
25 | /**
26 | * 如果配置文件不存在,写入
27 | * @return boolean 已存在或写入失败返回false 重新写入成功返回true
28 | */
29 | public static boolean writeConf(){
30 | if (!new File(Log4jConf).exists()) {
31 | return copyFile(TkConf.class.getResourceAsStream("/log4j.properties"),new File(Log4jConf));
32 | }
33 | return false;
34 | }
35 |
36 | /**
37 | * 获取项目根目录,如果是在Tomcat中运行,则返回部署根目录
38 | * @return String
39 | */
40 | private static String getRootPath() {
41 | String path="";
42 | if (TkConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF")!=-1) {
43 | path=TkConf.class.getClassLoader().getResource("").toString().substring(6, TkConf.class.getClassLoader().getResource("").toString().indexOf("WEB-INF"));
44 | path=path+"/";
45 | }else {
46 | String temp=System.getProperty("user.dir");
47 | temp=temp.replace("\\","/");
48 | path=temp+"/";
49 | }
50 | return path;
51 | }
52 |
53 | private static boolean copyFile(InputStream from, File to) {
54 | try {
55 | if (! to.getParentFile().exists()) {
56 | to.getParentFile().mkdirs();
57 | }
58 | OutputStream os = new FileOutputStream(to);
59 | byte[] buffer = new byte[65536];
60 | int count = from.read(buffer);
61 | while (count > 0) {
62 | os.write(buffer, 0, count);
63 | count = from.read(buffer);
64 | }
65 | os.close();
66 | return true;
67 | } catch (IOException e) {
68 | // e.printStackTrace();
69 | return false;
70 | }
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/dbunit/DbUnitUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.dbunit;
2 |
3 |
4 | import java.io.File;
5 | import java.io.FileOutputStream;
6 | import java.sql.Connection;
7 | import java.sql.SQLException;
8 |
9 | import org.dbunit.DatabaseUnitException;
10 | import org.dbunit.database.DatabaseConfig;
11 | import org.dbunit.database.DatabaseConnection;
12 | import org.dbunit.database.IDatabaseConnection;
13 | import org.dbunit.database.QueryDataSet;
14 | import org.dbunit.dataset.IDataSet;
15 | import org.dbunit.dataset.ITable;
16 | import org.dbunit.dataset.xml.FlatXmlDataSet;
17 | import org.dbunit.ext.mysql.MySqlDataTypeFactory;
18 | import org.dbunit.ext.mysql.MySqlMetadataHandler;
19 |
20 | import com.github.qihootest.leo.toolkit.dbunit.data.excel.XlsDataSet;
21 | import com.github.qihootest.leo.toolkit.mysql.ConnMysql;
22 | import com.github.qihootest.leo.toolkit.util.LogUtil;
23 |
24 | /**
25 | * 从数据库中导出到excel、xml文件、创建dbunit的DatabaseConnection、IDataSet
26 | * @author @bwgang (bwgang@163.com)
2013-02-07
27 | *
28 | */
29 | public class DbUnitUtil {
30 |
31 | private static final LogUtil log = LogUtil.getLogger(DbUnitUtil.class);
32 |
33 | //针对Mysql数据库专用的
34 |
35 | /**
36 | * 从数据库中读取数据写入到文件中,支持excel和xml格式,以文件后缀名区分
37 | * @param destFilePath 写入文件的路径
38 | * @param mysql 连接MySql数据库的实例
39 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
40 | */
41 | public static void writeToFileFromMysql(String destFilePath,ConnMysql mysql,String querySqlsSplitBySemicolon){
42 | writeToFileFromDataBase(destFilePath,mysql.getConnection(),querySqlsSplitBySemicolon);
43 | }
44 |
45 | /**
46 | * 根据connection和查询sql语句,返回ITable
47 | * @param mysql mysql数据库连接实例
48 | * @param resultName 查询结果集对应的名称
49 | * @param querySql 单个sql查询语句
50 | * @return ITable
51 | */
52 | public static ITable getTableFromMysql(ConnMysql mysql,String resultName,String querySql){
53 | return getTableFromDataBase(mysql.getConnection(), resultName, querySql);
54 | }
55 |
56 | /**
57 | * 根据connection和查询sql语句,返回IDataSet
58 | * @param mysql mysql数据库连接实例
59 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
60 | * @return IDataSet
61 | */
62 | public static IDataSet getDataSetFromMysql(ConnMysql mysql,String querySqlsSplitBySemicolon){
63 | return getDataSetFromDataBase(mysql.getConnection(), querySqlsSplitBySemicolon);
64 | }
65 |
66 | /**
67 | * 从Mysql实例转换为DatabaseConnection
68 | * @param mysql
69 | * @return DatabaseConnection
70 | */
71 | public static DatabaseConnection getDatabaseConnectionFromMysql(ConnMysql mysql) {
72 | return getDatabaseConnection(mysql.getConnection());
73 | }
74 |
75 |
76 | //根据jdbc的connection连接,参照上述Mysql的,其它数据库类型Oracle、MSSql等可复用
77 |
78 | /**
79 | * 从数据库中读取数据写入到文件中,支持excel和xml格式,以文件后缀名区分
80 | * @param databaseConnection 使用的IDatabaseConnection数据连接
81 | * @param destFilePath 写入文件的路径
82 | */
83 | public static void writeToFileFromDataBase(IDatabaseConnection databaseConnection,String destFilePath){
84 | IDataSet dataSet=null;
85 |
86 | try {
87 | dataSet=databaseConnection.createDataSet();
88 | } catch (Exception e) {
89 | log.error("未获取到测试集");
90 | log.error(e.getMessage());
91 | return;
92 | }
93 | writeToFileFromDataSet(dataSet,destFilePath);
94 | }
95 |
96 | /**
97 | * 从数据库中读取数据写入到文件中,支持excel和xml格式,以文件后缀名区分
98 | * @param destFilePath 写入文件的路径
99 | * @param databaseConnection 使用的IDatabaseConnection数据连接
100 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
101 | */
102 | public static void writeToFileFromDataBase(String destFilePath,IDatabaseConnection databaseConnection,String querySqlsSplitBySemicolon){
103 | writeToFileFromDataSet(getDataSetFromDataBase(databaseConnection, querySqlsSplitBySemicolon),destFilePath);
104 | }
105 |
106 | /**
107 | * 从数据库中读取数据写入到文件中,支持excel和xml格式,以文件后缀名区分
108 | * @param destFilePath 写入文件的路径
109 | * @param connection 使用的数据连接
110 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
111 | */
112 | public static void writeToFileFromDataBase(String destFilePath,Connection connection,String querySqlsSplitBySemicolon){
113 | writeToFileFromDataSet(getDataSetFromDataBase(connection, querySqlsSplitBySemicolon),destFilePath);
114 | }
115 |
116 | /**
117 | * 把数据集写入到文件中
118 | * @param dataSet
119 | * @param destFilePath
120 | */
121 | public static void writeToFileFromDataSet(IDataSet dataSet,String destFilePath) {
122 | File destFile = new File(destFilePath);
123 | try {
124 | String destFileName = destFile.getName();
125 | //转换为excel格式
126 | if(destFileName.endsWith(".xls")){
127 | XlsDataSet.write(dataSet, new FileOutputStream(destFile));
128 | }else if(destFileName.endsWith(".xml")){
129 | FlatXmlDataSet.write(dataSet, new FileOutputStream(destFile));
130 | }else{
131 | log.error("文件格式不是xls或者xml,不支持");
132 | return;
133 | }
134 | log.info("写入数据到文件 : "+destFile.getAbsolutePath());
135 | } catch (Exception e) {
136 | log.error("写入数据到文件失败");
137 | log.error(e.getMessage());
138 | }
139 | }
140 |
141 | /**
142 | * 根据connection和查询sql语句,返回IDataSet
143 | * @param connection 使用的数据连接
144 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
145 | * @return IDataSet
146 | */
147 | public static IDataSet getDataSetFromDataBase(Connection connection,String querySqlsSplitBySemicolon){
148 | return getDataSetFromDataBase(getDatabaseConnection(connection), querySqlsSplitBySemicolon);
149 | }
150 |
151 | /**
152 | * 根据databaseConnection和多个查询sql语句,返回IDataSet
153 | * @param databaseConnection 使用的IDatabaseConnection类型数据连接
154 | * @param querySqlsSplitBySemicolon 查询的sql语句,支持多个,使用;分开
155 | * @return queryDataSet
156 | */
157 | public static IDataSet getDataSetFromDataBase(IDatabaseConnection databaseConnection,String querySqlsSplitBySemicolon){
158 | //分割多个sql语句
159 | String[] querySqls = querySqlsSplitBySemicolon.split(";");
160 | QueryDataSet queryDataSet=null;
161 | try {
162 | queryDataSet = new QueryDataSet(databaseConnection);
163 | //处理多个语句情况
164 | for (int i = 0; i < querySqls.length; i++) {
165 | queryDataSet.addTable(getTableNameFromQuerySql(querySqls[i]), querySqls[i]);
166 | }
167 | } catch (Exception e) {
168 | log.error("查询数据失败!");
169 | log.error(e.getMessage());
170 | }
171 | return queryDataSet;
172 | }
173 |
174 | /**
175 | * 根据connection和查询sql语句,返回ITable
176 | * @param connection
177 | * @param resultName 指定查询结果集对应的表名
178 | * @param querySql 单个sql查询语句
179 | * @return ITable
180 | */
181 | public static ITable getTableFromDataBase(Connection connection,String resultName,String querySql){
182 | //分割多个sql语句
183 | ITable table=null;
184 | try {
185 | table=getDatabaseConnection(connection).createQueryTable(resultName, querySql);
186 | } catch (Exception e) {
187 | log.error("查询数据失败!");
188 | log.error(e.getMessage());
189 | }
190 | return table;
191 | }
192 |
193 | /**
194 | * 从connection转换为DatabaseConnection
195 | * @param connection
196 | * @return DatabaseConnection
197 | */
198 | public static DatabaseConnection getDatabaseConnection(Connection connection) {
199 | DatabaseConnection databaseConnection = null;
200 | try {
201 | databaseConnection= new DatabaseConnection(connection);
202 | DatabaseConfig config = databaseConnection.getConfig();
203 | //数据库为mysql
204 | if ("MySQL".equalsIgnoreCase(connection.getMetaData()
205 | .getDatabaseProductName())) {
206 | config.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER,
207 | new MySqlMetadataHandler());
208 | config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
209 | new MySqlDataTypeFactory());
210 | } //else if ("Oracle".equalsIgnoreCase(connection.getMetaData()
211 | // .getDatabaseProductName())) {
212 | // config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
213 | // new Oracle10DataTypeFactory());
214 | // }
215 | } catch (DatabaseUnitException | SQLException e) {
216 | log.error("创建dbunit的数据库连接失败");
217 | log.error(e.getMessage());
218 | return null;
219 | }
220 |
221 | return databaseConnection;
222 | }
223 |
224 |
225 |
226 | //私有方法
227 |
228 | /**
229 | * 从sql语句中查找到表名,第一个from之后的表名
230 | * @param querySql
231 | * @return String
232 | */
233 |
234 | private static String getTableNameFromQuerySql(String querySql){
235 | String tableName = null;
236 | String[] array = querySql.split(" ");
237 | for (int i = 0; i < array.length; i++) {
238 | if("From".equalsIgnoreCase(array[i].trim())){
239 | tableName = array[i+1].trim();
240 | break;
241 | }
242 | }
243 | return tableName;
244 |
245 | }
246 |
247 |
248 | }
249 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/dbunit/data/excel/XlsDataSet.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.dbunit.data.excel;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 |
9 | import org.apache.poi.hssf.usermodel.HSSFWorkbook;
10 | import org.dbunit.dataset.AbstractDataSet;
11 | import org.dbunit.dataset.DataSetException;
12 | import org.dbunit.dataset.DefaultTableIterator;
13 | import org.dbunit.dataset.IDataSet;
14 | import org.dbunit.dataset.ITable;
15 | import org.dbunit.dataset.ITableIterator;
16 | import org.dbunit.dataset.OrderedTableNameMap;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 |
20 | /**
21 | * 重写dbunit的XlsDataSet
22 | *
23 | */
24 | public class XlsDataSet extends AbstractDataSet {
25 |
26 | /**
27 | * Logger for this class
28 | */
29 | private static final Logger logger = LoggerFactory.getLogger(XlsDataSet.class);
30 |
31 | private final OrderedTableNameMap _tables;
32 |
33 | /**
34 | * 从Excel文件中创建XlsDataSet
35 | */
36 | public XlsDataSet(String file) throws IOException, DataSetException {
37 | this(new FileInputStream(file));
38 | }
39 |
40 | /**
41 | * 从Excel文件中创建XlsDataSet
42 | */
43 | public XlsDataSet(File file) throws IOException, DataSetException {
44 | this(new FileInputStream(file));
45 | }
46 |
47 |
48 | /**
49 | * Creates a new XlsDataSet object that loads the specified Excel document.
50 | */
51 | public XlsDataSet(InputStream in) throws IOException, DataSetException {
52 | _tables = super.createTableNameMap();
53 |
54 | HSSFWorkbook workbook = new HSSFWorkbook(in);
55 | int sheetCount = workbook.getNumberOfSheets();
56 | for (int i = 0; i < sheetCount; i++) {
57 | ITable table = new XlsTable(workbook, workbook.getSheetName(i),
58 | workbook.getSheetAt(i));
59 | _tables.add(table.getTableMetaData().getTableName(), table);
60 | }
61 | }
62 |
63 | //暂不使用部分
64 | /**
65 | * 从Excel的工作簿中创建XlsDataSet.
66 | */
67 | @Deprecated
68 | public XlsDataSet(HSSFWorkbook workbook) throws IOException, DataSetException {
69 | _tables = super.createTableNameMap();
70 |
71 | int sheetCount = workbook.getNumberOfSheets();
72 | for (int i = 0; i < sheetCount; i++) {
73 | ITable table = new XlsTable(workbook, workbook.getSheetName(i),
74 | workbook.getSheetAt(i));
75 | _tables.add(table.getTableMetaData().getTableName(), table);
76 | }
77 | }
78 |
79 | /**
80 | * 把数据集写入到Excel文档
81 | */
82 | public static void write(IDataSet dataSet, OutputStream out)
83 | throws IOException, DataSetException {
84 | logger.debug("write(dataSet={}, out={}) - start", dataSet, out);
85 |
86 | new XlsDataSetWriter().write(dataSet, out);
87 | }
88 |
89 | @SuppressWarnings("unchecked")
90 | @Override
91 | protected ITableIterator createIterator(boolean reversed)
92 | throws DataSetException {
93 | if (logger.isDebugEnabled())
94 | logger.debug("createIterator(reversed={}) - start", String.valueOf(reversed));
95 |
96 | ITable[] tables = (ITable[]) _tables.orderedValues().toArray(new ITable[0]);
97 | return new DefaultTableIterator(tables, reversed);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/dbunit/data/excel/XlsDataSetWriter.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.dbunit.data.excel;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 | import java.math.BigDecimal;
6 | import java.util.Date;
7 |
8 | import org.apache.poi.hssf.usermodel.HSSFCell;
9 | import org.apache.poi.hssf.usermodel.HSSFCellStyle;
10 | import org.apache.poi.hssf.usermodel.HSSFDataFormat;
11 | import org.apache.poi.hssf.usermodel.HSSFRichTextString;
12 | import org.apache.poi.hssf.usermodel.HSSFRow;
13 | import org.apache.poi.hssf.usermodel.HSSFSheet;
14 | import org.apache.poi.hssf.usermodel.HSSFWorkbook;
15 | import org.dbunit.dataset.Column;
16 | import org.dbunit.dataset.DataSetException;
17 | import org.dbunit.dataset.IDataSet;
18 | import org.dbunit.dataset.ITable;
19 | import org.dbunit.dataset.ITableIterator;
20 | import org.dbunit.dataset.ITableMetaData;
21 | import org.dbunit.dataset.datatype.DataType;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | /**
26 | * 说明:因为dbunit写xls文件使用的poi版本较早3.2版本,使用poi3.9时会写入xls文件失败
27 | * 拷贝其写excel的类到此,未做修改,写excel正常
28 | *
29 | * Writes an {@link IDataSet} to an XLS file or OutputStream.
30 | *
31 | * @author gommma (gommma AT users.sourceforge.net)
32 | * @author Last changed by: $Author: gommma $
33 | * @version $Revision: 915 $ $Date: 2008-12-07 14:17:25 +0100 (Sun, 07 Dec 2008) $
34 | * @since 2.4.0
35 | */
36 | public class XlsDataSetWriter
37 | {
38 | public static final String ZEROS = "0000000000000000000000000000000000000000000000000000";
39 |
40 | /**
41 | * A special format pattern used to create a custom {@link HSSFDataFormat} which
42 | * marks {@link Date} values that are stored via POI to an XLS file.
43 | * Note that it might produce problems if a normal numeric value uses this format
44 | * pattern incidentally.
45 | */
46 | public static final String DATE_FORMAT_AS_NUMBER_DBUNIT = "####################";
47 | /**
48 | * Logger for this class
49 | */
50 | private static final Logger logger = LoggerFactory.getLogger(XlsDataSetWriter.class);
51 |
52 | private HSSFCellStyle dateCellStyle;
53 |
54 | /**
55 | * Write the specified dataset to the specified Excel document.
56 | */
57 | public void write(IDataSet dataSet, OutputStream out)
58 | throws IOException, DataSetException
59 | {
60 | logger.debug("write(dataSet={}, out={}) - start", dataSet, out);
61 |
62 | HSSFWorkbook workbook = new HSSFWorkbook();
63 |
64 | this.dateCellStyle = createDateCellStyle(workbook);
65 |
66 | int index = 0;
67 | ITableIterator iterator = dataSet.iterator();
68 | while(iterator.next())
69 | {
70 | // create the table i.e. sheet
71 | ITable table = iterator.getTable();
72 | ITableMetaData metaData = table.getTableMetaData();
73 | HSSFSheet sheet = workbook.createSheet(metaData.getTableName());
74 |
75 | // write table metadata i.e. first row in sheet
76 | workbook.setSheetName(index, metaData.getTableName());
77 |
78 | HSSFRow headerRow = sheet.createRow(0);
79 | Column[] columns = metaData.getColumns();
80 | for (int j = 0; j < columns.length; j++)
81 | {
82 | Column column = columns[j];
83 | HSSFCell cell = headerRow.createCell(j);
84 | cell.setCellValue(new HSSFRichTextString(column.getColumnName()));
85 | }
86 |
87 | // write table data
88 | for (int j = 0; j < table.getRowCount(); j++)
89 | {
90 | HSSFRow row = sheet.createRow(j + 1);
91 | for (int k = 0; k < columns.length; k++)
92 | {
93 | Column column = columns[k];
94 | Object value = table.getValue(j, column.getColumnName());
95 | if (value != null)
96 | {
97 | HSSFCell cell = row.createCell(k);
98 | if(value instanceof Date){
99 | setDateCell(cell, (Date)value, workbook);
100 | }
101 | else if(value instanceof BigDecimal){
102 | setNumericCell(cell, (BigDecimal)value, workbook);
103 | }
104 | else if(value instanceof Long){
105 | setDateCell(cell, new Date( ((Long)value).longValue()), workbook);
106 | }
107 | else {
108 | cell.setCellValue(new HSSFRichTextString(DataType.asString(value)));
109 | }
110 | }
111 | }
112 | }
113 |
114 | index++;
115 | }
116 |
117 | // write xls document
118 | workbook.write(out);
119 | out.flush();
120 | }
121 |
122 | protected static HSSFCellStyle createDateCellStyle(HSSFWorkbook workbook) {
123 | HSSFDataFormat format = workbook.createDataFormat();
124 | short dateFormatCode = format.getFormat(DATE_FORMAT_AS_NUMBER_DBUNIT);
125 | HSSFCellStyle dateCellStyle = workbook.createCellStyle();
126 | dateCellStyle.setDataFormat(dateFormatCode);
127 | return dateCellStyle;
128 | }
129 |
130 | protected void setDateCell(HSSFCell cell, Date value, HSSFWorkbook workbook)
131 | {
132 | // double excelDateValue = HSSFDateUtil.getExcelDate(value);
133 | // cell.setCellValue(excelDateValue);
134 | // cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
135 |
136 | long timeMillis = value.getTime();
137 | cell.setCellValue( (double)timeMillis );
138 | cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
139 | cell.setCellStyle(this.dateCellStyle);
140 |
141 | // System.out.println(HSSFDataFormat.getBuiltinFormats());
142 | // TODO Find out correct cell styles for date objects
143 | // HSSFCellStyle cellStyleDate = workbook.createCellStyle();
144 | // cellStyleDate.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
145 | //
146 | // HSSFCellStyle cellStyleDateTimeWithSeconds = workbook.createCellStyle();
147 | // cellStyleDateTimeWithSeconds.setDataFormat(HSSFDataFormat.getBuiltinFormat("h:mm:ss"));
148 | //
149 | // HSSFDataFormat dataFormat = workbook.createDataFormat();
150 | // HSSFCellStyle cellStyle = workbook.createCellStyle();
151 | // cellStyle.setDataFormat(dataFormat.getFormat("dd/mm/yyyy hh:mm:ss"));
152 | //
153 | // SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
154 | // SimpleDateFormat formatter2 = new SimpleDateFormat("dd/MM/yyyy");
155 | // SimpleDateFormat formatter3 = new SimpleDateFormat("HH:mm:ss.SSS");
156 | //
157 | //
158 | // Date dateValue = (Date)value;
159 | // Calendar cal = null;
160 | //
161 | // // If it is a date value that does not have seconds
162 | // if(dateValue.getTime() % 60000 == 0){
163 | //// cellStyle = cellStyleDate;
164 | // cal=Calendar.getInstance();
165 | // cal.setTimeInMillis(dateValue.getTime());
166 | //
167 | // cell.setCellValue(cal);
168 | // cell.setCellStyle(cellStyle);
169 | //// cell.setCellValue(cal);
170 | // }
171 | // else {
172 | //// HSSFDataFormatter formatter = new HSSFDataFormatter();
173 | //
174 | // // If we have seconds assume that it is only h:mm:ss without date
175 | // // TODO Clean implementation where user can control date formats would be nice
176 | //// double dateDouble = dateValue.getTime() % (24*60*60*1000);
177 | // cal = get1900Cal(dateValue);
178 | //
179 | // String formatted = formatter3.format(dateValue);
180 | // //TODO Format ...
181 | //// cellStyle = cellStyleDateTimeWithSeconds;
182 | // System.out.println("date formatted:"+formatted);
183 | //// HSSFRichTextString s = new HSSFRichTextString(formatted);
184 | //// cell.setCellValue(s);
185 | // cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
186 | // cell.setCellValue((double)dateValue.getTime());
187 | // cell.setCellStyle(cellStyleDateTimeWithSeconds);
188 | // }
189 |
190 | }
191 |
192 | protected void setNumericCell(HSSFCell cell, BigDecimal value, HSSFWorkbook workbook)
193 | {
194 | if(logger.isDebugEnabled())
195 | logger.debug("setNumericCell(cell={}, value={}, workbook={}) - start",
196 | new Object[] {cell, value, workbook} );
197 |
198 | cell.setCellValue( ((BigDecimal)value).doubleValue() );
199 |
200 | HSSFDataFormat df = workbook.createDataFormat();
201 | int scale = ((BigDecimal)value).scale();
202 | short format;
203 | if(scale <= 0){
204 | format = df.getFormat("####");
205 | }
206 | else {
207 | String zeros = createZeros(((BigDecimal)value).scale());
208 | format = df.getFormat("####." + zeros);
209 | }
210 | if(logger.isDebugEnabled())
211 | logger.debug("Using format '{}' for value '{}'.", String.valueOf(format), value);
212 |
213 | HSSFCellStyle cellStyleNumber = workbook.createCellStyle();
214 | cellStyleNumber.setDataFormat(format);
215 | cell.setCellStyle(cellStyleNumber);
216 | }
217 |
218 |
219 | // public static Date get1900(Date date) {
220 | // Calendar cal = Calendar.getInstance();
221 | // cal.setTimeInMillis(date.getTime() % (24*60*60*1000));
222 | // cal.set(1900, 0, 1); // 1.1.1900
223 | // return cal.getTime();
224 | // }
225 | //
226 | // public static Calendar get1900Cal(Date date) {
227 | // Calendar cal = Calendar.getInstance();
228 | // cal.clear();
229 | //// long hoursInMillis = date.getTime() % (24*60*60*1000);
230 | //// long smallerThanDays = date.getTime() % (24*60*60*1000);
231 | //// cal.setTimeInMillis(date.getTime() % (24*60*60*1000));
232 | // cal.set(Calendar.SECOND, (int) (date.getTime() % (24*60*60*1000)) / (1000) );
233 | // cal.set(Calendar.MINUTE, (int) (date.getTime() % (24*60*60*1000)) / (1000*60) );
234 | // cal.set(Calendar.HOUR, (int) (date.getTime() % (24*60*60*1000)) / (1000*60*60) );
235 | //// cal.set(1900, 0, 1); // 1.1.1900
236 | // System.out.println(cal.isSet(Calendar.DATE));
237 | // return cal;
238 | // }
239 |
240 | private static String createZeros(int count) {
241 | return ZEROS.substring(0, count);
242 | }
243 |
244 | }
245 |
246 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/dbunit/data/excel/XlsTableWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Alibaba Group Holding Limited.
3 | * All rights reserved.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | *
17 | */
18 | package com.github.qihootest.leo.toolkit.dbunit.data.excel;
19 |
20 |
21 | import org.apache.commons.lang.StringUtils;
22 | import org.dbunit.dataset.AbstractTable;
23 | import org.dbunit.dataset.DataSetException;
24 | import org.dbunit.dataset.DefaultTableMetaData;
25 | import org.dbunit.dataset.ITable;
26 | import org.dbunit.dataset.ITableMetaData;
27 |
28 | import com.github.qihootest.leo.toolkit.QtafException;
29 |
30 | /**
31 | * 表名称后的数据源为获取数据表名称
32 | *
33 | */
34 | public class XlsTableWrapper extends AbstractTable {
35 | private ITable delegate;
36 | private String tableName;
37 |
38 | public XlsTableWrapper(String tableName, ITable table) {
39 | this.delegate = table;
40 | this.tableName = tableName;
41 | }
42 | public int getRowCount() {
43 | return delegate.getRowCount();
44 | }
45 |
46 | public ITableMetaData getTableMetaData() {
47 | ITableMetaData meta = delegate.getTableMetaData();
48 | try {
49 | return new DefaultTableMetaData(tableName, meta.getColumns(), meta.getPrimaryKeys());
50 | } catch (DataSetException e) {
51 | throw new QtafException("Don't get the meta info from " + meta, e);
52 | }
53 | }
54 |
55 | public Object getValue(int row, String column) throws DataSetException {
56 | Object delta = delegate.getValue(row, column);
57 | if (delta instanceof String) {
58 | if (StringUtils.isEmpty((String) delta)) {
59 | return null;
60 | }
61 | }
62 | return delta;
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/httpclient/ResponseInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.httpclient;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * http请求发送信息及返回信息
7 | * @author @bwgang (bwgang@163.com)
8 | *
9 | */
10 | public class ResponseInfo {
11 |
12 | private String httpUrl;//发起get或post请求的url信息,post的body信息转换为参数对字符串标识
13 | private String resBodyInfo;//http请求返回的body信息
14 | private Map resHeaderInfo;//http请求返回的Header键值对
15 | private String cookies; //http请求返回的cookie信息
16 | private String errMsgInfo;//记录错误信息
17 | public String getHttpUrl() {
18 | return httpUrl;
19 | }
20 | public void setHttpUrl(String httpUrl) {
21 | this.httpUrl = httpUrl;
22 | }
23 | public String getResBodyInfo() {
24 | return resBodyInfo;
25 | }
26 | public void setResBodyInfo(String resBodyInfo) {
27 | this.resBodyInfo = resBodyInfo;
28 | }
29 | public Map getResHeaderInfo() {
30 | return resHeaderInfo;
31 | }
32 | public void setResHeaderInfo(Map resHeaderInfo) {
33 | this.resHeaderInfo = resHeaderInfo;
34 | }
35 | public String getCookies() {
36 | return cookies;
37 | }
38 | public void setCookies(String cookies) {
39 | this.cookies = cookies;
40 | }
41 | public String getErrMsgInfo() {
42 | return errMsgInfo;
43 | }
44 | public void setErrMsgInfo(String errMsgInfo) {
45 | this.errMsgInfo = errMsgInfo;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/mysql/ConnMysql.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.mysql;
2 |
3 | import java.sql.Connection;
4 | import java.sql.DriverManager;
5 | import java.sql.ResultSet;
6 | import java.sql.SQLException;
7 |
8 | import com.github.qihootest.leo.toolkit.util.LogUtil;
9 |
10 | import com.mysql.jdbc.Statement;
11 |
12 | /**
13 | * 说明:连接数据库配置信息
14 | *
15 | * @author lianghui (lianghui@360.cn) 2013-01-10
16 | *
17 | */
18 | public class ConnMysql {
19 |
20 | private static Connection con = null; // 创建用于连接数据库的Connection对象
21 | private static LogUtil log = LogUtil.getLogger(ConnMysql.class);// 日志记录
22 |
23 | /** IP地址 */
24 | public String ip = "127.0.0.1";
25 | /** 端口号 */
26 | public String port = "3306";
27 | /** 数据库名 */
28 | public String dataName = "";
29 | /** 用户名 */
30 | public String userName = "root";
31 | /** 密码 */
32 | public String passWord = "";
33 |
34 | /**
35 | * 默认构造方法
36 | */
37 | public ConnMysql() {
38 |
39 | }
40 |
41 | /**
42 | * 构造方法
43 | *
44 | * @param ip
45 | * @param port
46 | * @param dataName
47 | * @param userName
48 | * @param passWord
49 | */
50 | public ConnMysql(String ip, String port, String dataName, String userName,
51 | String passWord) {
52 | this.ip = ip;
53 | this.port = port;
54 | this.dataName = dataName;
55 | this.userName = userName;
56 | this.passWord = passWord;
57 | }
58 |
59 | /**
60 | * 说明:数据库连接信息,并创建数据库连接 编码为UTF-8
61 | *
62 | * @return Connection
63 | */
64 | public Connection getConnection() {
65 | try {
66 | // 如果连接未释放,先断开连接
67 | if (con != null) {
68 | // con.close();
69 | // con=null;
70 | // log.info("手动关闭数据库连接成功");
71 | return con;// 未关闭时,不再重新连接,直接返回
72 | }
73 | Class.forName("com.mysql.jdbc.Driver");// 加载Mysql数据驱动
74 | con = DriverManager
75 | .getConnection(
76 | "jdbc:mysql://"
77 | + ip
78 | + ":"
79 | + port
80 | + "/"
81 | + dataName
82 | + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=round",
83 | userName, passWord);// 创建数据连接
84 | } catch (Exception e) {
85 | log.error("数据库连接失败");
86 | log.error(e.getMessage());
87 | }
88 | return con; // 返回所建立的数据库连接
89 |
90 | }
91 |
92 | /**
93 | * 说明:关闭数据库连接
94 | */
95 | public void closedConn() {
96 |
97 | try {
98 | if (con != null)
99 | con.close();
100 | con = null;
101 | log.info("关闭数据库连接成功【" + this.ip + "_" + this.port + "_"
102 | + this.dataName + "】");
103 | } catch (SQLException e) {
104 | log.error("关闭数据库连接失败【" + this.ip + "_" + this.port + "_"
105 | + this.dataName + "】");
106 | log.error(e.getMessage());
107 | }
108 | }
109 |
110 | /**
111 | * 说明:用于执行插入、更新、删除的sql语句,当受影响的行数为0和执行失败时返回false
112 | *
113 | * @param sql
114 | * @return boolean 成功返回true,失败返回false
115 | */
116 | public boolean excSql(String sql) {
117 | if (con == null)
118 | getConnection(); // 连接到数据库
119 |
120 | try {
121 | Statement st = (Statement) con.createStatement(); // 创建用于执行静态sql语句的Statement对象
122 | int counts = st.executeUpdate(sql); // 执行操作的sql语句
123 | if (0 == counts) {
124 | log.info("执行成功,,共0条数据受到影响,没有完成操作!:SQL语句-->【" + sql + "】");
125 | return false;
126 | }
127 | log.info("执行成功,共" + counts + "条数据受到影响:" + "SQL语句-->【" + sql + "】");
128 | return true;
129 | } catch (SQLException e) {
130 | log.error("执行失败:SQL语句-->【" + sql + "】");
131 | log.error(e.getMessage());
132 | return false;
133 | }
134 |
135 | }
136 |
137 | /**
138 | * 说明:用户select的查询语句,返回查询结果集
139 | *
140 | * @param sql
141 | * @return ResultSet 查询结果集
142 | */
143 | public ResultSet querySql(String sql) {
144 |
145 | ResultSet rs = null;
146 | if (con == null)
147 | getConnection(); // 连接到数据库
148 | try {
149 | Statement st = null; // 创建用于执行静态sql语句的Statement对象,st属局部变量
150 | st = (Statement) con
151 | .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
152 | ResultSet.CONCUR_READ_ONLY);
153 | rs = st.executeQuery(sql); // 执行sql查询语句,返回查询数据的结果集
154 | log.info("执行成功:SQL查询语句-->【" + sql + "】");
155 | } catch (SQLException e) {
156 | log.error("执行失败:SQL查询语句-->【" + sql + "】");
157 | log.error(e.getMessage());
158 | rs = null;
159 | }
160 | return rs;
161 | }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/com/github/qihootest/leo/toolkit/mysql/MysqlUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.qihootest.leo.toolkit.mysql;
2 |
3 | import java.sql.Connection;
4 | import java.sql.ResultSet;
5 | import java.sql.ResultSetMetaData;
6 | import java.sql.SQLException;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.TreeMap;
11 |
12 | import com.github.qihootest.leo.toolkit.util.LogUtil;
13 | import com.github.qihootest.leo.toolkit.util.StringUtil;
14 |
15 | /**
16 | * 数据库操作类,需先使用connMysql或reConnMysql方法先连接到数据库后,再执行其他操作
17 | * @author @bwgang (bwgang@163.com)
18 | *
19 | */
20 | public class MysqlUtil {
21 |
22 | private static Connection con = null; // 创建用于连接数据库的Connection对象
23 | private static LogUtil log = LogUtil.getLogger(MysqlUtil.class);// 日志记录
24 | private ConnMysql mysql;// 数据库连接
25 |
26 | /**
27 | * 连接mysql,如果已连接,则不再根据传入的参数创建连接
28 | *
29 | * @param ip
30 | * @param port
31 | * @param dataName
32 | * @param userName
33 | * @param passWord
34 | */
35 | public void connMysql(String ip, String port, String dataName,
36 | String userName, String passWord) {
37 | if (con == null) {
38 | if (null == mysql) {//
39 | mysql = new ConnMysql();
40 | mysql.ip = ip;
41 | mysql.port = port;
42 | mysql.dataName = dataName;
43 | mysql.userName = userName;
44 | mysql.passWord = passWord;
45 | }
46 | con = mysql.getConnection();
47 | }
48 | }
49 |
50 | /**
51 | * 根据连接信息,重新连接mysql,如果已有连接,则先断开
52 | *
53 | * @param ip
54 | * @param port
55 | * @param dataName
56 | * @param userName
57 | * @param passWord
58 | */
59 | public void reConnMysql(String ip, String port, String dataName,
60 | String userName, String passWord) {
61 | MysqlUtil mu = new MysqlUtil();
62 | closedConn();
63 | mu.connMysql(ip, port, dataName, userName, passWord);
64 | }
65 |
66 | /**
67 | * 说明:关闭数据库连接
68 | */
69 | public void closedConn() {
70 | if (con != null) {
71 | mysql.closedConn();
72 | con = null;
73 | }
74 | }
75 |
76 | /**
77 | * 说明:用于执行插入、更新、删除的sql语句,当受影响的行数为0和执行失败时返回false
78 | *
79 | * @param sql
80 | * @return boolean 成功返回true,失败返回false
81 | */
82 | public boolean excSql(String sql) {
83 | if (null == con) {
84 | // log.info("尚未连接数据库");
85 | return false;// 数据库未连接
86 | }
87 | return mysql.excSql(sql);
88 | }
89 |
90 | /**
91 | * 按指定的sql,查询表中所有数据,返回表中所有列
92 | *
93 | * @param sql
94 | * @return List