项目结构
结构图
框架实现
1.数据准备 和 参数输入
数据准备有三种方式:
- 一:通过访问数据库,获取我们需要的接口测试参数,比如测试登录接口,我们要提前获得 用户名和密码,然后才能连同我们的url发送给服务端。
要想访问数据库,必须要先与数据库建立连接,我这里采用的是JDBC 和C3P0,可以参考我之前的blog
Java通过JDBC 进行MySQL数据库操作
Java数据库连接池 学习笔记
- 二:直接将所需要的数据写入到xml或者excel中,然后写一个工具类直接去读取就行。
例如 :要准备 测试接口输入参数为:email和password
(1)编写paramsData.xml,将上述参数放在xml中进行管理
<?xml version="1.0" encoding="UTF-8"?>
<map>
<bean beanName="signIn">
<locator name="email" value="charlie.chen@dji.com"></locator>
<locator name="password" value="123456"></locator>
</bean>
</map>
(2)封装一个XmlUtil工具类负责读取xml ,使用第三方的jar包dom4j,XmlUtil中readXMLDocument方法返回值为Map
public static Map<String, String> readXMLDocument(String xmlPath, String beanName) throws IOException {
}
上述readXMLDocument方法中,参数xmlName为xml文件的名字; 参数beanName为xml文件中节点的名称。
- 三:通过xml管理测试数据,也可以用Excel进行管理
和xml管理数据类似:封装一个ExcelUtil工具类负责读取excel ,使用第三方的jar包poi,ExcelUtil中readExcel方法返回值为List
// 读取Excel中数据
public static List<ParamBean> readExcel(String excelPath) throws Exception {
}
2.请求执行
(1)封装一个CookieUtil工具类,通过CookieStore储存cookie
CookieUtil类中setCookieStore方法返回值为CookieStore
**
* 将服务端返回的SessionID存储在CookieStore中
* @author Charlie.chen
* @date 2016-10-31
*
*/
public class CookieUtil {
private static CookieStore cookieStore = null;
private static LogUtil log = new LogUtil(CookieUtil.class);
/*
* 通过CookieStore储存cookie
*/
public static CookieStore setCookieStore(HttpResponse httpResponse) {
log.info("setCookieStore");
cookieStore = new BasicCookieStore();
// JSESSIONID
String setCookie = httpResponse.getFirstHeader("Set-Cookie").getValue();
String JSESSIONID = setCookie.substring("JSESSIONID=".length(),
setCookie.indexOf(";"));
log.debug("JSESSIONID:" + JSESSIONID);
// 新建一个Cookie
BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", JSESSIONID);
cookie.setVersion(0);
cookie.setDomain("127.0.0.1");
cookie.setPath("/CwlProClient");
cookieStore.addCookie(cookie);
return cookieStore;
}
}
上述setCookieStore方法中,httpResponse参数为服务端响应值,类型为HttpResponse
(2)用httpClient简单封装一个httpClientUtil工具类有get.post,put,delete方法
public static CloseableHttpResponse doGet(String url, Map<String, String> paramsMap, CloseableHttpClient httpclient, CookieStore cookieStore) {
}
public static CloseableHttpResponse doPost(String url, Map<String, String> paramsMap, CloseableHttpClient httpclient,CookieStore cookieStore) {
}
public static CloseableHttpResponse doPut(String url, Map<String, String> paramsMap, CloseableHttpClient httpclient, CookieStore cookieStore) {
}
public static CloseableHttpResponse doDelete(String url, Map<String, String> paramsMap, CloseableHttpClient httpclient, CookieStore cookieStore) {
}
/**
* 封装一个获取请求实体的方法
* @param params
* @param ucode
* @return
*/
public static UrlEncodedFormEntity getFormEntity(Map<String, String> paramsMap, Charset... ucode) {
}
3.结果解析 和 验证
(1)将从服务端返回的HttpResponse结果解析为ResponseBean对象
public class ReponseUtil {
private static LogUtil log = new LogUtil(ReponseUtil.class);
private static ResponseBean responseBean=null;
public static ResponseBean setResponseBean(CloseableHttpResponse httpResponse) {
// 使用响应对象获取响应实体
HttpEntity entity = httpResponse.getEntity();
if (entity != null)
try {
// 将响应实体转为字符串
String responseString = EntityUtils.toString(entity, "utf-8");
String rs = responseString.replace("\r\n", "");
responseBean = new ResponseBean();
responseBean.setStatus(httpResponse.getStatusLine().getReasonPhrase());
responseBean.setStatusCode(Integer.toString(httpResponse.getStatusLine().getStatusCode()));
responseBean.setBody(rs);
log.info("\n" + "***************************返回开始**********************************" + "\n"
+ httpResponse.getStatusLine().getReasonPhrase() + "\n"
+ Integer.toString(httpResponse.getStatusLine().getStatusCode()) + "\n" + "Context" + rs + "\n"
+ "***************************返回结束**********************************");
HeaderIterator iterator = httpResponse.headerIterator();
while (iterator.hasNext()) {
log.debug("\t" + iterator.next());
}
} catch (Exception e) {
e.printStackTrace();
}
return responseBean;
}
}
(2)结果验证
主要验证 HttpResponse的状态status,状态码statusCode和 响应实体body,通过第三方断言jar包AsertJ
// add Assert
Assert.assertEquals("OK", responseBean.getStatus());
Assert.assertEquals("200", responseBean.getStatusCode());
Assert.assertEquals("dsgfdfgdfsdfdgfdg", responseBean.getBody());
4.测试用例
测试用例管理使用了testNG管理 ,使用了TestNG参数化测试,通过xml文件来执行case
(1)测试case脚本
public class NewTest {
static CookieStore cookieStore=null;
static CloseableHttpClient httpclient=null;
@Test
public void test() {
try {
String url = PropertiesUtil.getValue("url","config.properties");
String xmlPath=NewTest.class.getClassLoader().getResource("paramData.xml").getPath();
Map<String, String> paramsMap = XmlUtil.readXMLDocument(xmlPath, "signIn");
httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
CloseableHttpResponse httpResponse = HttpClientUtil.doPost(url, paramsMap, httpclient, cookieStore);
ResponseBean responseBean = ReponseUtil.setResponseBean(httpResponse);
// add Assert
Assert.assertEquals("OK", responseBean.getStatus());
Assert.assertEquals("200", responseBean.getStatusCode());
Assert.assertEquals("dsgfdfgdfsdfdgfdg", responseBean.getBody());
} catch (IOException e) {
e.printStackTrace();
}
}
@BeforeClass
public void beforeClass() {
}
@AfterClass
public void afterClass() {
}
@BeforeTest
public void beforeTest() {
}
@AfterTest
public void afterTest() {
}
@BeforeSuite
public void beforeSuite() {
/*
* 登录进入系统获取JSESSIONID放入到CookieStore中
* */
}
@AfterSuite
public void closeClient() {
try {
// 关闭流并释放资源
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2)testng.xml文件的编写
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="false" preserve-order="true">
<test name="Test">
<classes>
<class name="com.dji.itester.cases.NewTest">
<methods>
<include name="test" />
</methods>
</class>
</classes>
</test>
</suite>
右键->run as ->TestNG Suite,这个场景的的测试用例就可以运行了
5.失败用例重跑
关于失败用例重跑,在我前面的blog TestNG实现用例运行失败自动截图和重跑 写的很清楚了,这里就不详说了,大概步骤如下:
(1)新建TestNGRetry类继承IRetryAnalyzer,实现用例失败自动重跑逻辑
(2)添加用例重跑监听器RetryListener,用例失败自动重跑功能
(3)在testng.xml文件中配置自己编写的监听器
<listeners>
<listener class-name="com.dji.itester.runfail.TestNGListener" />
<listener class-name="com.dji.itester.runfail.RetryListener"/>
</listeners>
6.源码管理 和 Jenkins配置
(1)源码管理
将代码上传到github上进行托管,关于github的使用参考我前面的blog Git 学习笔记)
(2)Jenkins配置
运行jenkins,首先创建一个job为ITester-DJI
将代码上传到github上进行托管,然后在jenkins上配置clone 到你本地或者远程的jenkins来:
构建触发器
Poll SCM: 定时检查源码变更(根据SCM软件的版本号),如果有更新就checkout最新code下来,然后执行构建动作。Build periodically:周期进行项目构建(它不care源码是否发生变化)
我的配置如下: 每天凌晨2:00进行一次构建
构建
构建后操作,发送邮件
7.测试报告和邮件
这里用到第三方的jar包ReportNG,对测试报告进行定制化。添加插件,关联testNg.xml,添加ReportNg的监听器,修改最后的TestNG的报告。
<build>
<plugins>
<!-- 添加插件,关联testNg.xml,添加ReportNg的监听器,修改最后的TestNG的报告 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Dfile.encoding=UTF-8</argLine>
<suiteXmlFiles>
<!--<suiteXmlFile>testng.xml</suiteXmlFile> -->
<suiteXmlFile>src/test/java/com/dji/itester/testSuites/${xmlFileName}</suiteXmlFile>
</suiteXmlFiles>
<properties>
<property>
<name>usedefaultlisteners</name>
<value>false</value>
</property>
<property>
<name>listener</name>
<value>org.uncommons.reportng.HTMLReporter,
org.uncommons.reportng.JUnitXMLReporter</value>
</property>
</properties>
<workingDirectory>target/</workingDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
测试报告: