接口框架實(shí)現(xiàn)整體步驟:
resource層:
底層是application.properties配置路徑文件
databaseconfig.xml配置數(shù)據(jù)庫文件
mapper文件夾中是sqlmapper.xml的配置文件
testng.xml配置文件 測試類的配置文件
還有moco的接口文件,json格式的,需要一個jar包依賴
utils層:
configfile類是從application.properties配置文件中獲取url和參數(shù)信息房匆,string、json漫萄、map等類型
databaseutil類是加載database.xml文件,處理從數(shù)據(jù)庫中獲取數(shù)據(jù)信息
model層:
對應(yīng)的實(shí)體類是與數(shù)據(jù)庫的表一致盈匾、還可以定義接口名的枚舉類和參數(shù)url的枚舉類
config層:
配置測試報告意系,extenttestngrepoter監(jiān)聽器類
case層:
寫具體的接口類牙躺,用httpclient去實(shí)現(xiàn)每個接口的調(diào)用和結(jié)果驗證
接口與接口之間的數(shù)據(jù)依賴可以通過testng的數(shù)據(jù)依賴去傳遞。
一、創(chuàng)建maven項目颤霎,配置pom文件
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20170516</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
<dependency>
<groupId>com.relevantcodes</groupId>
<artifactId>extentreports</artifactId>
<version>2.41.1</version>
</dependency>
<dependency>
<groupId>com.vimalselvam</groupId>
<artifactId>testng-extentsreport</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.9.10</version>
</dependency>
二唇撬、創(chuàng)建application.properties文件
test.url=http://localhost:8086
#登錄接口
login.uri=/demo/login
#更新用戶信息接口
updateUserInfo.uri=/demo/updateUserInfo
#獲取用戶列表接口
getUserList.uri=/demo/getUserInfo
#獲取用戶信息接口
getUserInfo.uri=/demo/getUserInfo
#添加用戶接口
addUser.uri=/demo/addUser
三筋栋、創(chuàng)建databaseConfig.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 注冊對象的空間命名 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 1.加載數(shù)據(jù)庫驅(qū)動 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 2.數(shù)據(jù)庫連接地址 -->
<property name="url" value="jdbc:mysql://localhost:3306/dbgirl"/>
<!-- 數(shù)據(jù)庫用戶... -->
<property name="username" value="rootname"/>
<!-- 數(shù)據(jù)庫密碼... -->
<property name="password" value="passname"/>
</dataSource>
</environment>
</environments>
<!-- 注冊映射文件:java對象與數(shù)據(jù)庫之間的xml文件路徑咒彤! -->
<mappers>
<mapper resource="mapper/SQLMapper.xml"/>
</mappers>
</configuration>
四、在resource下創(chuàng)建mapper文件夾劈伴,創(chuàng)建SQLMapper.xml文件密末,先不寫內(nèi)容
testng.xml文件也可以先建立
五、利用springboot中的spring-data-jpa創(chuàng)建實(shí)體類,創(chuàng)建數(shù)據(jù)庫表严里,可以參考在《springboot學(xué)習(xí)(四)》中的代碼新啼。
也可以手動去創(chuàng)建數(shù)據(jù)庫表。
六刹碾、在java下創(chuàng)建cases燥撞、config、model迷帜、utils文件夾物舒,分別放不同的內(nèi)容
現(xiàn)在model中創(chuàng)建對應(yīng)數(shù)據(jù)庫表的實(shí)體類,代碼如下:
只舉其中一個表的例子,字段值要與數(shù)據(jù)庫表的值一致
package com.course.model;
import lombok.Data;
@Data
public class User {
private Integer id; //id是主鍵戏锹,并且會自增冠胯,調(diào)接口時不需要傳參數(shù)id,傳其他參數(shù)就可以
private String userName;
private String password;
private Integer age;
private String sex;
private Integer permission;
private Integer isDelete;
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", permission=" + permission +
", isDelete=" + isDelete +
'}';
}
}
該代碼中沒有寫get和set方法景用,是因為用了lombok可以自動省略get和set方法
下面會用到logincase的model類涵叮,補(bǔ)充一下源碼:
package com.course.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "loginCase")
public class loginCase {
@Id
@GeneratedValue
private Integer id;
private String userName;
private String password;
private String expected;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getExpected() {
return expected;
}
public void setExpected(String expected) {
this.expected = expected;
}
}
在model中寫一個枚舉類,把接口寫成枚舉類型伞插,保證傳進(jìn)來的接口名稱正確
package com.course.model;
public enum InterfaceName {
LOGIN,UPDATEUSERINFO,GETUSERLIST,GETUSERINFO,ADDUSER
}
七、在config文件中創(chuàng)建ExtentTestNGIReporterListener類盾碗,測試報告會用到
Maven pom文件中添加依賴:
<dependency>
<groupId>com.relevantcodes</groupId>
<artifactId>extentreports</artifactId>
<version>2.41.1</version>
</dependency>
<dependency>
<groupId>com.vimalselvam</groupId>
<artifactId>testng-extentsreport</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>3.0.6</version>
</dependency>
在resource中增加testng.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="用戶管理系統(tǒng)測試套件">
<test name="用戶管理系統(tǒng)測試用例">
<classes>
<class name="com.course.cases.LoginTest">
<include name="LoginTrue" />
<include name="LoginFalse" />
</class>
<class name="com.course.cases.AddUserTest">
<include name="AddUser" />
</class>
<class name="com.course.cases.GetUserInfoTest">
<include name="getUserInfo" />
</class>
<class name="com.course.cases.GetUserListTest">
<include name="getUserList" />
</class>
<class name="com.course.cases.updateUserInfo">
<include name="updateUserInfo" />
</class>
</classes>
</test>
<listeners>
<listener class-name="com.course.config.ExtentTestNGIReporterListener"/>
</listeners>
</suite>
<listener class-name="com.course.config.ExtentTestNGIReporterListener"/>
中ExtentTestNGIReporterListener類的路徑媚污,就是下面要在config文件夾下創(chuàng)建的ExtentTestNGIReporterListener類的路徑。
在config文件夾下增加監(jiān)聽類:不用改類中的內(nèi)容:
package com.course.config;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.model.TestAttribute;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import com.aventstack.extentreports.reporter.configuration.Theme;
import org.testng.*;
import org.testng.xml.XmlSuite;
import java.io.File;
import java.util.*;
public class ExtentTestNGIReporterListener implements IReporter {
//生成的路徑以及文件名
private static final String OUTPUT_FOLDER = "test-output/";
private static final String FILE_NAME = "index.html";
private ExtentReports extent;
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
init();
boolean createSuiteNode = false;
if(suites.size()>1){
createSuiteNode=true;
}
for (ISuite suite : suites) {
Map<String, ISuiteResult> result = suite.getResults();
//如果suite里面沒有任何用例廷雅,直接跳過耗美,不在報告里生成
if(result.size()==0){
continue;
}
//統(tǒng)計suite下的成功、失敗航缀、跳過的總用例數(shù)
int suiteFailSize=0;
int suitePassSize=0;
int suiteSkipSize=0;
ExtentTest suiteTest=null;
//存在多個suite的情況下商架,在報告中將同一個一個suite的測試結(jié)果歸為一類,創(chuàng)建一級節(jié)點(diǎn)芥玉。
if(createSuiteNode){
suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
}
boolean createSuiteResultNode = false;
if(result.size()>1){
createSuiteResultNode=true;
}
for (ISuiteResult r : result.values()) {
ExtentTest resultNode;
ITestContext context = r.getTestContext();
if(createSuiteResultNode){
//沒有創(chuàng)建suite的情況下蛇摸,將在SuiteResult的創(chuàng)建為一級節(jié)點(diǎn),否則創(chuàng)建為suite的一個子節(jié)點(diǎn)灿巧。
if( null == suiteTest){
resultNode = extent.createTest(r.getTestContext().getName());
}else{
resultNode = suiteTest.createNode(r.getTestContext().getName());
}
}else{
resultNode = suiteTest;
}
if(resultNode != null){
resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());
if(resultNode.getModel().hasCategory()){
resultNode.assignCategory(r.getTestContext().getName());
}else{
resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
}
resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
//統(tǒng)計SuiteResult下的數(shù)據(jù)
int passSize = r.getTestContext().getPassedTests().size();
int failSize = r.getTestContext().getFailedTests().size();
int skipSize = r.getTestContext().getSkippedTests().size();
suitePassSize += passSize;
suiteFailSize += failSize;
suiteSkipSize += skipSize;
if(failSize>0){
resultNode.getModel().setStatus(Status.FAIL);
}
resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
}
buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);
buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);
buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);
}
if(suiteTest!= null){
suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
if(suiteFailSize>0){
suiteTest.getModel().setStatus(Status.FAIL);
}
}
}
// for (String s : Reporter.getOutput()) {
// extent.setTestRunnerOutput(s);
// }
extent.flush();
}
private void init() {
//文件夾不存在的話進(jìn)行創(chuàng)建
File reportDir= new File(OUTPUT_FOLDER);
if(!reportDir.exists()&& !reportDir .isDirectory()){
reportDir.mkdir();
}
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
// 設(shè)置靜態(tài)文件的DNS
//怎么樣解決cdn.rawgit.com訪問不了的情況
htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
htmlReporter.config().setDocumentTitle("api自動化測試報告");
htmlReporter.config().setReportName("api自動化測試報告");
htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
htmlReporter.config().setTheme(Theme.STANDARD);
htmlReporter.config().setCSS(".node.level-1 ul{ display:none;} .node.level-1.active ul{display:block;}");
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true);
}
private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {
//存在父節(jié)點(diǎn)時赶袄,獲取父節(jié)點(diǎn)的標(biāo)簽
String[] categories=new String[0];
if(extenttest != null ){
List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
categories = new String[categoryList.size()];
for(int index=0;index<categoryList.size();index++){
categories[index] = categoryList.get(index).getName();
}
}
ExtentTest test;
if (tests.size() > 0) {
//調(diào)整用例排序,按時間排序
Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
@Override
public int compare(ITestResult o1, ITestResult o2) {
return o1.getStartMillis()<o2.getStartMillis()?-1:1;
}
});
treeSet.addAll(tests.getAllResults());
for (ITestResult result : treeSet) {
Object[] parameters = result.getParameters();
String name="";
//如果有參數(shù)抠藕,則使用參數(shù)的toString組合代替報告中的name
for(Object param:parameters){
name+=param.toString();
}
if(name.length()>0){
if(name.length()>50){
name= name.substring(0,49)+"...";
}
}else{
name = result.getMethod().getMethodName();
}
if(extenttest==null){
test = extent.createTest(name);
}else{
//作為子節(jié)點(diǎn)進(jìn)行創(chuàng)建時饿肺,設(shè)置同父節(jié)點(diǎn)的標(biāo)簽一致,便于報告檢索盾似。
test = extenttest.createNode(name).assignCategory(categories);
}
//test.getModel().setDescription(description.toString());
//test = extent.createTest(result.getMethod().getMethodName());
for (String group : result.getMethod().getGroups())
test.assignCategory(group);
List<String> outputList = Reporter.getOutput(result);
for(String output:outputList){
//將用例的log輸出報告中
test.debug(output);
}
if (result.getThrowable() != null) {
test.log(status, result.getThrowable());
}
else {
test.log(status, "Test " + status.toString().toLowerCase() + "ed");
}
test.getModel().setStartTime(getTime(result.getStartMillis()));
test.getModel().setEndTime(getTime(result.getEndMillis()));
}
}
}
private Date getTime(long millis) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(millis);
return calendar.getTime();
}
}
要先把test-output文件夾展示出來敬辣,需要配置idea:
配置testng報告,進(jìn)入idea,按著圖中去點(diǎn)擊
選擇左側(cè)測試方法溉跃,點(diǎn)擊listener汰聋,勾選Use default reporters,然后點(diǎn)擊確定
右鍵執(zhí)行testng.xml文件喊积,就能看到test-output文件夾烹困,查看生成的報告:
右鍵點(diǎn)擊index.html選擇copy path,粘貼路徑在瀏覽器中查看報告信息乾吻。
八髓梅、在utils文件中創(chuàng)建configFile,從配置文件中利用resourcebundle取url绎签,代碼如下
package com.course.utils;
import com.course.model.InterfaceName;
import java.util.Locale;
import java.util.ResourceBundle;
public class configFile {
//從application文件中取url
private static ResourceBundle resourceBundle = ResourceBundle.getBundle("application", Locale.CHINA);
public static String getUrl(InterfaceName name){
//取域名和端口號
String baseurl = resourceBundle.getString("test.url");
String uri = "";
//如果傳進(jìn)來的接口名稱正確枯饿,就返回相應(yīng)接口的url
if(name == InterfaceName.LOGIN){
uri = resourceBundle.getString("login.uri");
}
if(name == InterfaceName.UPDATEUSERINFO){
uri = resourceBundle.getString("updateUserInfo.uri");
}
if(name == InterfaceName.GETUSERLIST){
uri = resourceBundle.getString("getUserList.uri");
}
if (name == InterfaceName.GETUSERINFO){
uri = resourceBundle.getString("getUserInfo.uri");
}
if(name == InterfaceName.ADDUSER){
uri = resourceBundle.getString("addUser.uri");
}
//拼接url
String testurl = baseurl + uri;
return testurl;
}
}
九、在utils中創(chuàng)建DatabaseUtil诡必,從databaseconfig.xml配置文件中取配置信息奢方,創(chuàng)建sqlsession并返回,在case類中就不用再寫獲取數(shù)據(jù)庫信息了
package com.course.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
/*
把sqlsession封裝起來爸舒,從數(shù)據(jù)庫中獲取數(shù)據(jù)
*/
public class DatabaseUtil {
public static SqlSession getsqlSession() throws IOException {
//獲取配置的資源文件
Reader reader = Resources.getResourceAsReader("databaseConfig.xml");
//得到SqlSessionFactory蟋字,使用類加載器加載xml文件
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
//得到sqlsession對象,這個對象就能執(zhí)行配置文件中的sql語句啦
SqlSession session = factory.openSession();
return session;
}
}
十扭勉、在cases文件夾下寫測試類鹊奖,先寫一個loginTest
package com.course.cases;
import com.course.config.TestConfig;
import com.course.model.InterfaceName;
import com.course.model.loginCase;
import com.course.utils.DatabaseUtil;
import com.course.utils.configFile;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.ibatis.session.SqlSession;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
public class LoginTest {
//加@BeforeTest注解,在用例執(zhí)行前先獲取接口的url
@BeforeTest(groups = "loginTrue",description = "登錄接口")
public void beforeTest(){
//獲取接口地址
TestConfig.loginuri = configFile.getUrl(InterfaceName.LOGIN);
TestConfig.addUseruri = configFile.getUrl(InterfaceName.ADDUSER);
TestConfig.getUserInfouri = configFile.getUrl(InterfaceName.GETUSERINFO);
TestConfig.getUserListuri = configFile.getUrl(InterfaceName.GETUSERLIST);
TestConfig.updateUserInfouri = configFile.getUrl(InterfaceName.UPDATEUSERINFO);
TestConfig.defaultHttpClient = new DefaultHttpClient();
}
@Test(groups = "loginTrue", description = "登錄成功的接口")
public void LoginTrue() throws IOException {
//定義sqlsession對象涂炎,從DatabaseUtil類中調(diào)getsqlSession方法忠聚,直接獲取到sqlsession對象,可以執(zhí)行sql語句唱捣,不需要配置數(shù)據(jù)庫信息
SqlSession sqlSession = DatabaseUtil.getsqlSession();
//調(diào)selectone方法两蟀,通過mybatis中的sql語句查數(shù)據(jù)庫表,并返回結(jié)果賦值給logincase對象
//sqlstatement與SQLMapper.xml中的id一致震缭,33是傳的參數(shù)id
loginCase loginCase = sqlSession.selectOne("logincase",33);
//打印查詢的結(jié)果赂毯,結(jié)果就是從數(shù)據(jù)庫中查詢到的結(jié)果
System.out.println(loginCase.toString());
System.out.println(TestConfig.loginuri);
}
//調(diào)數(shù)據(jù)庫表中第二行數(shù)據(jù),可以認(rèn)為是用戶名和密碼錯誤蛀序,登錄失敗的接口
@Test(groups = "loginFalse",description = "登錄失敗的接口")
public void LoginFalse() throws IOException {
SqlSession sqlSession = DatabaseUtil.getsqlSession();
loginCase loginCase = sqlSession.selectOne("logincase",36);
System.out.println(loginCase.toString());
System.out.println(TestConfig.loginuri);
}
}
其中TestConfig.loginuri等屬性欢瞪,是在config文件夾下定義了一個TestConfig的類,給該類創(chuàng)建了多個uri名稱的屬性徐裸,這樣uri名稱就不用隨便再起遣鼓,直接從這個類中調(diào)用就行。
package com.course.config;
import org.apache.http.client.CookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
public class TestConfig {
public static String loginuri;
public static String updateUserInfouri;
public static String getUserListuri;
public static String getUserInfouri;
public static String addUseruri;
public static DefaultHttpClient defaultHttpClient;
public static CookieStore cookieStore;
}
十一重贺、執(zhí)行l(wèi)oginTest類時遇到問題:
報org.apache.ibatis.exceptions.PersistenceException:的錯誤
是因為mybatis沒有連接上數(shù)據(jù)庫骑祟,或者是數(shù)據(jù)庫和實(shí)體類的對應(yīng)關(guān)系沒有建立起來
需要修改databaseconfig.xml文件,數(shù)據(jù)庫url為:
<property name="url" value="jdbc:mysql://localhost:3306/dbgirl?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8"/>
之前該url是寫在yml中回懦,&符號不需要轉(zhuǎn)義,寫到xml中需要對 & 轉(zhuǎn)義成&
還需要加上
<typeAliases>
<typeAlias type="com.course.model.loginCase" />
<typeAlias type="com.course.model.addUserCase" />
</typeAliases>
要加到environments配置前面次企,要不然會報錯:
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHand......
<typeAlias type="com.course.model.addUserCase" /> 要寫多個怯晕,哪個實(shí)體類對應(yīng)的有數(shù)據(jù)庫表都要單獨(dú)寫,要不然也會報錯缸棵。
同時mapper/SQLMapper.xml中的namespace也要寫對com.course.model
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.course.model">
<select id="logincase" parameterType="Integer" resultType="com.course.model.loginCase">
select * from loginCase where id=#{id}
</select>
</mapper>
十二舟茶、HttpClient的學(xué)習(xí)總結(jié):
以上的框架中缺少moco接口,請參考《Moco接口框架應(yīng)用實(shí)戰(zhàn)》堵第。
下面會在前十一章搭建框架的基礎(chǔ)上吧凉,利用HttpClient去調(diào)接口驗證測試。
12.1 moco的接口:
登錄接口
還有一個調(diào)列表接口踏志,不再貼圖:
TestDologin 測試類:
public class TestDologin {
//設(shè)置全局變量cookieStore
public static CookieStore cookieStore;
// //返回token值
// public static String token;
@BeforeTest(groups = "loginTrue")
public void beforetest(){
//獲取所有接口的uri
TestConfig.doLoginuri = ConfigFile.GetUrl(InterfaceName.DOLOGIN);
TestConfig.getMerchantListuri = ConfigFile.GetUrl(InterfaceName.GETMERCHANTLIST);
//獲取所有接口的參數(shù)值,doLoginParams接口是post入?yún)son格式的阀捅,可以從配置文件中調(diào),getMerchantListuri中已拼接了接口
TestConfig.doLoginParams = ConfigFile.GetParams(ParamsName.DOLOGINPARAMS);
}
/*
從配置文件中獲取全部的參數(shù)轉(zhuǎn)換成String類型入?yún)? */
@Test(groups = "loginTrue", description = "登錄成功的接口")
public void testdologin() throws IOException {
System.out.println(TestConfig.doLoginuri);
System.out.println(TestConfig.doLoginParams);
DefaultHttpClient httpClient = new DefaultHttpClient();
//該接口返回的有cookie针余,獲取cookie饲鄙,賦值給全局變量,下一個接口setcookiestore圆雁,下一個接口入?yún)r使用cookie
cookieStore = httpClient.getCookieStore();
//調(diào)moco的接口可以用這個
HttpPost httpPost = new HttpPost(TestConfig.doLoginuri);
//設(shè)置參數(shù)
//使用json入?yún)⑷碳叮雲(yún)⑹侵形臅r不行。
//設(shè)置請求頭header
httpPost.setHeader("Content-Type","application/json;charset=gbk");
//傳入?yún)?shù)
StringEntity entity=new StringEntity(TestConfig.doLoginParams.toString(),"gbk");
httpPost.setEntity(entity);
//獲取返回結(jié)果
HttpResponse httpResponse = httpClient.execute(httpPost);
String result = EntityUtils.toString(httpResponse.getEntity());
System.out.println(result);
}
}
TestgetMerchantList測試類:
package com.course.cases;
import com.course.config.TestConfig;
import com.course.utils.DatabaseUtil;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSession;
import org.json.JSONArray;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import org.apache.http.client.CookieStore;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
public class TestgetMerchantList {
public static int MerchantId;
@Test(dependsOnGroups = "loginTrue",description = "獲取列表接口")
public void TestgetMerchantList() throws URISyntaxException, IOException {
System.out.println(TestConfig.getMerchantListuri);
DefaultHttpClient httpClient = new DefaultHttpClient();
//參數(shù)已經(jīng)拼接在url中
HttpGet httpGet = new HttpGet(TestConfig.getMerchantListuri);
//通過moco接口摸柄,可以使用setcookiestore方法從上一個接口獲取token值
httpClient.setCookieStore(TestDologin.cookieStore);
//調(diào)取真實(shí)的接口只能設(shè)置header颤练,傳值token
// httpGet.setHeader("token",TestDologin.token);
HttpResponse response = httpClient.execute(httpGet);
/*
下面要驗證幾種斷言方法:
1、判斷接口返回是否是200
2驱负、判斷一頁返回的門店列表有20條
3、判斷從第5位開始評分由大到小
4患雇、判斷與數(shù)據(jù)庫中獲取數(shù)據(jù)是否一致
*/
//1跃脊、判斷接口返回是否是200
Assert.assertEquals(response.getStatusLine().getStatusCode(),200,"接口返回200成功");
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
JSONObject resultobject = new JSONObject(result);
JSONObject p2pdataobject = resultobject.getJSONObject("p2pdata");
JSONArray dataArray = p2pdataobject.getJSONArray("data");
//2、判斷一頁返回的門店列表有20條
Assert.assertEquals(dataArray.length(),20,"一頁返回門店列表有20條");
List scorelist = new ArrayList();
for (int i=0;i<dataArray.length();i++){
String dataresult = dataArray.get(i).toString();
JSONObject dataresultobject = new JSONObject(dataresult);
scorelist.add(dataresultobject.getInt("score"));
}
//3苛吱、判斷從第5位開始評分由大到小
for (int j=4;j<scorelist.size()-1;j++){
if ((Integer)scorelist.get(j) >= (Integer)scorelist.get(j+1)){
System.out.println("比對成功");
}else{
System.out.println("比對不成功");
}
}
String dataresult = dataArray.get(1).toString();
JSONObject dataresultobject = new JSONObject(dataresult);
MerchantId = dataresultobject.getInt("id");
System.out.println(MerchantId);
//4酪术、判斷與數(shù)據(jù)庫中獲取數(shù)據(jù)是否一致
SqlSession sqlSession = DatabaseUtil.getsqlsession();
int marchantid = sqlSession.selectOne("getmarchantid",MerchantId);
Assert.assertEquals(marchantid,MerchantId);
}
}
總結(jié):入?yún)⒌奶幚矸椒ㄓ泻芏喾N:
get請求:
1、把參數(shù)拼接在url中
getMerchantListuri=getMerchantList?longitude=116.41090393066406&latitude=39.96870040893555
2翠储、部分參數(shù)拼接在url中绘雁,其他參數(shù)和url拼接到一起
//url要拼接參數(shù)
URIBuilder uriBuidler = new URIBuilder(TestConfig.getCarWashDetailuri);
//需要從其他類中依賴的參數(shù)值
uriBuidler.setParameter("merchantId", String.valueOf(TestgetMerchantList.MerchantId));
HttpGet httpGet = new HttpGet(uriBuidler.build());
3、定義一個List<NameValuePair>援所,傳入?yún)?shù)
URIBuilder uriBuilder = new URIBuilder(TestConfig.getCarWashDetailuritwouri);
//第一種寫入?yún)?shù)的方法庐舟,定義一個List,類型是NameValuePair
List<NameValuePair> urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("merchantId", String.valueOf(TestgetMerchantList.MerchantId)));
urlParameters.add(new BasicNameValuePair("channel","6"));
uriBuilder.setParameters(urlParameters);
HttpGet httpGet = new HttpGet(uriBuilder.build());
post請求:
1、獲取object類型的參數(shù)
配置文件中這些寫:
doLoginParams={"phone": "15677777777","validateCode": "9612"}
寫一個工具類住拭,定義一個獲取object參數(shù)的方法
//定義一個靜態(tài)的方法挪略,獲取post接口的參數(shù)值历帚,返回類型是Object類型
public static Object GetParams(ParamsName name){
if (name.equals(ParamsName.DOLOGINPARAMS)){
Object doLoginParams = resourceBundle.getObject("doLoginParams");
return doLoginParams;
}
return null;
}
在httpclient中入?yún)⒅苯舆@樣寫:
TestConfig.doLoginParams = ConfigFile.GetParams(ParamsName.DOLOGINPARAMS);
//傳入?yún)?shù)
StringEntity entity=new StringEntity(TestConfig.doLoginParams.toString(),"gbk");
httpPost.setEntity(entity);
注:直接配置文件中取String類型的參數(shù)值也行,不用傳object的也可以
2杠娱、配置文件中寫部分參數(shù)
doLoginParamstwo={"phone": "15677777777"}
工具類中寫挽牢,定義一個返回map類型參數(shù)的方法:
//定義一個靜態(tài)的方法,獲取post接口的參數(shù)值摊求,返回類型為map禽拔,這樣有些參數(shù)可以依賴其他的接口,或者手動填寫參數(shù)
public static Map<String,Object> GetParamswithMap(ParamsName name){
if (name == ParamsName.DOLOGINPARAMSTWOPARAMS){
String doLoginParams = resourceBundle.getString("doLoginParamstwo");
return JSON.parseObject(doLoginParams);
}
return null;
}
在httpclient中入?yún)⒅苯舆@樣寫:
TestConfig.doLoginParamstwo = ConfigFile.GetParamswithMap(ParamsName.DOLOGINPARAMSTWOPARAMS);
//傳入?yún)?shù)
Map<String,Object> map = TestConfig.doLoginParamstwo;
map.put("validateCode",TestSendVerificationCode.validate_code);//也可以使用數(shù)據(jù)依賴傳參數(shù)
StringEntity entity=new StringEntity(map.toString(),"gbk");
httpPost.setEntity(entity);
十三室叉、TestNg的擴(kuò)展學(xué)習(xí)總結(jié):
1睹栖、添加依賴:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.9.10</version>
</dependency>
2、寫一個方法太惠,使用@Test注解
package com.course.LearnTestNgCases;
import org.testng.annotations.Test;
/*
test注解
*/
public class BasicAnnotation {
@Test
public void testcaseone(){
System.out.println("這是最基本的注解磨淌,用來把方法標(biāo)記為測試的一部分");
}
}
3、@BeforeMethod @AfterMethod注解
package com.course.LearnTestNgCases;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/*
test注解
*/
public class BasicAnnotation {
@Test
public void testcaseone(){
System.out.println("這是最基本的注解凿渊,用來把方法標(biāo)記為測試的一部分");
}
@Test
public void testcasetwo(){
System.out.println("第二個測試方法");
}
@BeforeMethod
public void beforemethod(){
System.out.println("測試方法之前運(yùn)行");
}
@AfterMethod
public void aftermethod(){
System.out.println("測試方法之后運(yùn)行");
}
}
測試結(jié)果為:
4梁只、@BeforeClass @AfterClass 注解
package com.course.LearnTestNgCases;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;
/*
test注解
*/
public class BasicAnnotation {
public BasicAnnotation(){
System.out.println("kkkkkk");
}
@Test
public void testcaseone(){
System.out.println("這是最基本的注解,用來把方法標(biāo)記為測試的一部分");
}
@Test
public void testcasetwo(){
System.out.println("第二個測試方法");
}
@BeforeMethod
public void beforemethod(){
System.out.println("測試方法之前運(yùn)行");
}
@AfterMethod
public void aftermethod(){
System.out.println("測試方法之后運(yùn)行");
}
@BeforeClass
public void beforeclass(){
System.out.println("在類之前運(yùn)行");
}
@AfterClass
public void afterclass(){
System.out.println("在類之后運(yùn)行");
}
}
測試結(jié)果:
5埃脏、@BeforeTest搪锣、@AfterTest注解
package com.course.LearnTestNgCases;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;
/*
test注解
*/
public class BasicAnnotation {
@Test
public void testcaseone(){
System.out.println("這是最基本的注解,用來把方法標(biāo)記為測試的一部分");
}
@Test
public void testcasetwo(){
System.out.println("第二個測試方法");
}
@BeforeSuite
public void beforesuite(){
System.out.println("beforesuite測試套件彩掐,在BeforeClass類之前運(yùn)行");
}
@AfterSuite
public void aftersuite(){
System.out.println("aftersuite測試套件构舟,在Afterclass類之后運(yùn)行");
}
@BeforeTest
public void beforetest(){
System.out.println("beforetest在測試之前運(yùn)行");
}
@AfterTest
public void aftertest(){
System.out.println("aftertest在測試之后運(yùn)行");
}
@BeforeClass
public void beforeclass(){
System.out.println("在類之前運(yùn)行");
}
@AfterClass
public void afterclass(){
System.out.println("在類之后運(yùn)行");
}
@BeforeMethod
public void beforemethod(){
System.out.println("測試方法之前運(yùn)行");
}
@AfterMethod
public void aftermethod(){
System.out.println("測試方法之后運(yùn)行");
}
}
測試結(jié)果:
6、@BeforeSuite堵幽、@AfterSuite注解
package com.course.LearnTestNgCases;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.testng.annotations.*;
/*
test注解
*/
public class BasicAnnotation {
@Test
public void testcaseone(){
System.out.println("這是最基本的注解狗超,用來把方法標(biāo)記為測試的一部分");
}
@Test
public void testcasetwo(){
System.out.println("第二個測試方法");
}
@BeforeMethod
public void beforemethod(){
System.out.println("測試方法之前運(yùn)行");
}
@AfterMethod
public void aftermethod(){
System.out.println("測試方法之后運(yùn)行");
}
@BeforeClass
public void beforeclass(){
System.out.println("在類之前運(yùn)行");
}
@AfterClass
public void afterclass(){
System.out.println("在類之后運(yùn)行");
}
@BeforeSuite
public void beforesuite(){
System.out.println("beforesuite測試套件,在BeforeClass類之前運(yùn)行");
}
@AfterSuite
public void aftersuite(){
System.out.println("aftersuite測試套件朴下,在Afterclass類之后運(yùn)行");
}
}
測試結(jié)果:
總結(jié):
testng的注解執(zhí)行順序如下:
BeforeSuite > BeforeTest > BeforeClass > BeforeMethod > Test
測試套件 > 測試組 > 測試類 > 測試方法 >具體的測試方法
7努咐、套件測試:先創(chuàng)建3個類,其中一個是SuiteConfig類
package com.course.LearnTestNgCases.Suite;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
/*
測試套件的配置
*/
public class SuiteConfig {
@BeforeSuite
public void beforesuite(){
System.out.println("在測試套件前執(zhí)行");
}
@AfterSuite
public void aftersuite(){
System.out.println("在測試套件后執(zhí)行");
}
}
另外兩個是LoginTest類和PayTest類
package com.course.LearnTestNgCases.Suite;
import org.testng.annotations.Test;
/*
測試類
*/
public class LoginTest {
@Test
public void logintest(){
System.out.println("登錄成功");
}
}
package com.course.LearnTestNgCases.Suite;
import org.testng.annotations.Test;
public class PayTest {
@Test
public void paytest(){
System.out.println("支付成功");
}
}
創(chuàng)建suite.xml 名稱可以隨意起殴胧,該文件中寫測試類執(zhí)行的順序:
與上面講的注解順序一致渗稍。
把suite.xml文件寫全,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="學(xué)習(xí)testng的suite測試套件">
<test name="測試LoginTest類" >
<classes>
<class name="com.course.LearnTestNgCases.Suite.LoginTest">
<include name="com.course.LearnTestNgCases.Suite.LoginTest.logintest"/>
</class>
<class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
</classes>
</test>
<test name="測試PayTest類">
<classes>
<class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
<class name="com.course.LearnTestNgCases.Suite.PayTest"/>
</classes>
</test>
</suite>
右鍵運(yùn)行suite.xml文件团滥,查看運(yùn)行結(jié)果:
正常情況下竿屹,這些寫會比較合理:
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="學(xué)習(xí)testng的suite測試套件">
<test name="測試LoginTest類" >
<classes>
<class name="com.course.LearnTestNgCases.Suite.SuiteConfig"/>
<class name="com.course.LearnTestNgCases.Suite.LoginTest"/>
<class name="com.course.LearnTestNgCases.Suite.PayTest"/>
</classes>
</test>
</suite>
運(yùn)行結(jié)果:
8、忽略測試
@Test(enabled = false) 忽略測試
@Test(enabled = true) 執(zhí)行測試
package com.course.LearnTestNgCases;
import org.testng.annotations.Test;
public class IgnoreTest {
@Test
public void ignore1(){
System.out.println("執(zhí)行測試");
}
@Test(enabled = false)
public void ignore2(){
System.out.println("enable=false 忽略測試");
}
@Test(enabled = true)
public void ignore3(){
System.out.println("enable=true 執(zhí)行測試");
}
}
執(zhí)行結(jié)果:
9灸姊、testng組測試
9.1拱燃、方法分組
在方法上加@Test(groups="groupsname")進(jìn)行分組
@BeforeGroups(“groupsname”) 、@AfterGroups(“groupsname”)注解厨钻,在對應(yīng)的測試組執(zhí)行之前執(zhí)行扼雏,在對應(yīng)的測試組執(zhí)行之后執(zhí)行
package com.course.LearnTestNgCases.Groups;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
public class TestGroups {
@Test(groups = "server")
public void testserver(){
System.out.println("服務(wù)端測試用例");
}
@Test(groups = "client")
public void testclient(){
System.out.println("客戶端測試用例");
}
@BeforeGroups("server")
public void beforeservergroups(){
System.out.println("在服務(wù)端測試用例執(zhí)行之前執(zhí)行");
}
@AfterGroups("server")
public void afterservergroups(){
System.out.println("在服務(wù)端測試用例執(zhí)行之后執(zhí)行");
}
@BeforeGroups("client")
public void beforeclientgroups(){
System.out.println("在客戶端測試用例執(zhí)行之前執(zhí)行");
}
@AfterGroups("client")
public void afterclientgroups(){
System.out.println("在客戶端測試用例執(zhí)行之后執(zhí)行");
}
}
groups組的名字要一致坚嗜。
執(zhí)行結(jié)果:
9.2 類分組
先建3個類,在每個類上加不同的groups诗充,在testng.xml中通過配置groups來分組執(zhí)行測試用例
groupclass1類,@Test(groups = "student"):
package com.course.LearnTestNgCases.Groups;
import org.testng.annotations.Test;
@Test(groups = "student")
public class groupclass1 {
public void testgroupclass1(){
System.out.println("這是student分組下的測試用例1");
}
}
groupclass2類苍蔬,@Test(groups = "student")
package com.course.LearnTestNgCases.Groups;
import org.testng.annotations.Test;
@Test(groups = "student")
public class groupclass2 {
public void testgroupclass2(){
System.out.println("這是student分組下的測試用例2");
}
}
groupclass3類,@Test(groups = "teacher"),和上面兩個類的分組不同
package com.course.LearnTestNgCases.Groups;
import org.testng.annotations.Test;
@Test(groups = "teacher")
public class groupclass3 {
public void testgroupclass3(){
System.out.println("這是teacher分組下的測試用例");
}
}
新建一個xml蝴蜓,添加groups配置碟绑,只包含student組
右鍵執(zhí)行該xml,查看結(jié)果中只執(zhí)行了groupclass1和groupclass2兩個類茎匠,groupclass3類沒有執(zhí)行格仲。
加上teacher分組,再次執(zhí)行就能看到執(zhí)行了groupclass3類
10诵冒、異常測試:
執(zhí)行中有異常凯肋,期望測試結(jié)果就是異常,用@Test(expectedExceptions = RuntimeException.class)注解使測試用例執(zhí)行結(jié)果為正常
package com.course.LearnTestNgCases;
import org.testng.annotations.Test;
/*
異常測試汽馋,期望測試結(jié)果就是異常侮东,用@Test(expectedExceptions = RuntimeException.class)注解使測試用例執(zhí)行結(jié)果為正常
*/
public class Exceptiontest {
@Test(expectedExceptions = RuntimeException.class)
public void testExceptiontest(){
System.out.println("執(zhí)行時會有異常,預(yù)期結(jié)果就是希望有異常豹芯,所以測試用例會執(zhí)行通過悄雅。本用例要拋出一個異常");
//主動拋出一個異常或者制造一個異常
// throw new RuntimeException();
int a=10;
int b=0;
int c=a/b;//b為0铁蹈,肯定會報異常宽闲。
}
}
11、依賴測試
11.1 依賴方法
dependsOnMethods = {"testfirst"}
代碼如下:
@Test
public void testfirst(){
System.out.println("testfirst方法執(zhí)行通過后再執(zhí)行下面的方法握牧,執(zhí)行失敗不執(zhí)行下面的方法");
}
@Test(dependsOnMethods = {"testfirst"})
public void testsencond(){
System.out.println("testsencond方法依賴testfirst方法");
}
11.2 依賴組
dependsOnGroups = "testthird"
代碼如下:
@Test(groups = "testthird")
public void testthird(){
System.out.println("testthird方法執(zhí)行通過后再執(zhí)行下面依賴該組的方法容诬,執(zhí)行失敗不執(zhí)行下面的方法");
}
@Test(dependsOnGroups = "testthird")
public void testfourth(){
System.out.println("testfourth方法依賴testthird方法");
}
12、參數(shù)化測試
12.1 參數(shù)化測試沿腰,xml文件參數(shù)化
在xml中寫入:
在類中寫入@parameter({"name","age"})注解放案,輸入?yún)?shù)
12.2 參數(shù)化測試,@DataProvider方法
先寫一個dataprovider方法矫俺,定義一個name值,返回類型必須是Object[][]或者Iterator<Object[]>掸冤,參數(shù)個數(shù)可以為一個或者多個
//數(shù)據(jù)方法
@DataProvider(name = "data")
public Object[][] dataprovider(){
Object[][] objects = new Object[][]{
{"zhaowei",40,"kkk",180},
{"suyoupeng",42,"jjj",170},
{"linxinru",43,"ppp",160}
};
return objects;
}
測試用例方法,@Test注解中要寫(dataProvider = "data")厘托,data名稱與上面定義的name值一致,參數(shù)值傳了3遍稿湿,執(zhí)行結(jié)果有三條記錄:
//測試用例
@Test(dataProvider = "data")
public void testdataprovider(String name,int age,String address,int height){
System.out.println("通過DataProvider傳過來的name是"+name+",age是"+age+",地址是"+address+",身高是"+height);
}
定義兩個方法铅匹,分別傳入不同的值,通過dataprovider傳值
方法代碼如下:
@Test(dataProvider = "datatwo")
public void testdologin(String username,String password){
System.out.println("登錄成功饺藤,用戶名是"+username+",密碼是"+password);
}
@Test(dataProvider = "datatwo")
public void testgetmerchantlist(int merchantid,String merchantname,String merchantaddress){
System.out.println("查詢商戶列表成功包斑,商戶id是"+merchantid+",商戶名稱是"+merchantname+",商戶地址是"+merchantaddress);
}
dataprovider方法為:
//數(shù)據(jù)方法二,根據(jù)不同的方法傳不同的值進(jìn)來
@DataProvider(name = "datatwo")
public Object[][] dataprovidertwo(Method method){
Object[][] result = null;
if (method.getName().equals("testdologin")){
result = new Object[][]{
{"18600228000","123456"}
};
}else if (method.getName().equals("testgetmerchantlist")){
result = new Object[][]{
{38159,"秦小姐洗車門店","北京市通州區(qū)"}
};
}
return result;
}
根據(jù)調(diào)Method類的getName方法獲取不同方法的名稱流礁,判斷不同的名稱返回不同的值。
擴(kuò)展@dataprovider的使用:
12.3 從另一個類中獲取數(shù)據(jù)依賴
第一個類中專門寫數(shù)據(jù)
package com.course.LearnTestNgCases.GroupsAndDataprovider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/*
專門提供數(shù)據(jù)的類罗丰,在使用數(shù)據(jù)信息的地方用@Test(dataProvider = "data",dataProviderClass = class111.class)來引入
*/
public class class111 {
@DataProvider(name = "data")
public static Object[][] dataprovider(){
Object[][] objects = new Object[][]{
{"zhaowei",40,"kkk",180},
{"suyoupeng",42,"jjj",170},
{"linxinru",43,"ppp",160}
};
return objects;
}
}
第二個類用@Test(dataProvider = "data",dataProviderClass = class111.class)來引入測試數(shù)據(jù)
package com.course.LearnTestNgCases.GroupsAndDataprovider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class class222 {
//測試用例神帅,從class111類中引入測試數(shù)據(jù)
@Test(dataProvider = "data",dataProviderClass = class111.class)
public void testdataprovider(String name,int age,String address,int height){
System.out.println("通過DataProvider傳過來的name是"+name+",age是"+age+",地址是"+address+",身高是"+height);
}
}
12.4 返回類型為迭代器Iterator<Object[]>
在class111類中寫入如下代碼:
@DataProvider(name="getmerchatidAndname")
public static Iterator<Object[]> getmerchatidAndnameDataProvider() throws IOException {
//定義Object[]類型的list
List<Object[]> result=new ArrayList<Object[]>();
//創(chuàng)建sqlsession的實(shí)例
SqlSession session= DatabaseUtil.getsqlsession();
//從數(shù)據(jù)庫中獲取數(shù)據(jù)信息
List<Object> alldata=session.selectList("getmerchatidAndname");
//從數(shù)據(jù)庫中獲取的數(shù)據(jù)類型是Object,該方法返回的類型是Object[],所以需要做類型轉(zhuǎn)換
//用foreach循環(huán)對alldata中的每一條數(shù)據(jù)做形式轉(zhuǎn)換萌抵,并存儲到result中
for (Object u : alldata) {
//做一個形式轉(zhuǎn)換
result.add(new Object[] { u });
}
//然后result的迭代器類型的數(shù)據(jù)
return result.iterator();
//下面的代碼與上面的代碼功能類似就是做一個形式轉(zhuǎn)換找御,從Object類型轉(zhuǎn)換成Object[]類型
//生成一個迭代器,對迭代器中的每一個數(shù)據(jù)做形式轉(zhuǎn)換
//迭代器的方法有hasNext()是否有值绍填,有值為真霎桅,沒值為假,it.next()獲取具體的值
// Iterator it=alldata.iterator();
// while(it.hasNext()){
// result.add(new Object[] { it.next() });
// }
// return result.iterator();
}
然后在model層建一個實(shí)體類:
package com.course.model;
public class Cfwshop {
private int id;
private String shop_name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getShop_name() {
return shop_name;
}
public void setShop_name(String shop_name) {
this.shop_name = shop_name;
}
@Override
public String toString() {
return "Cfwshop{" +
"id=" + id +
", shop_name='" + shop_name + '\'' +
'}';
}
}
再在SQLMapper.xml中寫sql語句,id與session.selectList("getmerchatidAndname");中的值一致讨永。
<select id="getmerchatidAndname" resultType="com.course.model.Cfwshop">
select id,shop_name from autosvc.cfw_shop LIMIT 10;
</select>
再在測試類中調(diào)dataprovider數(shù)據(jù)類,入?yún)㈩愋褪荂fwshop cfwshop 實(shí)體類滔驶,通過get方法獲取具體值。
//測試用例卿闹,從class111類中引入Iterator類型的測試數(shù)據(jù)
//需要創(chuàng)建一個Cfwshop的實(shí)體類揭糕,這樣才能接收到具體的值
@Test(dataProvider = "getmerchatidAndname",dataProviderClass = class111.class)
public void testgetmerchatidAndname(Cfwshop cfwshop){
System.out.println("商戶id為:"+cfwshop.getId()+",商戶名稱為:"+cfwshop.getShop_name());
}
13、多線程測試
@Test(invocationCount = 10,threadPoolSize = 3)
package com.course.LearnTestNgCases.MultiThread;
import org.testng.annotations.Test;
/*
驗證使用多線程
*/
public class MultiThreadOnAnnotation {
//設(shè)置10個線程比原,3個線程池插佛,從線程池中取3個線程,用3個線程執(zhí)行10遍測試用例,如果不設(shè)置threadPoolSize量窘,就會默認(rèn)用一個線程執(zhí)行10遍雇寇,所以要設(shè)置一個線程池
@Test(invocationCount = 10,threadPoolSize = 3)
public void testMultiThreadOnAnnotation(){
System.out.println("1");
System.out.printf("Thread id: %s%n",Thread.currentThread().getId());
}
}
在xml中通過配置線程數(shù)來控制線程:
先寫一個測試類,有3個測試方法:
package com.course.LearnTestNgCases.MultiThread;
import org.testng.annotations.Test;
public class MultiThreadOnXml {
@Test
public void test1(){
System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
}
@Test
public void test2(){
System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
}
@Test
public void test3(){
System.out.printf("Thread Id : %s%n",Thread.currentThread().getId());
}
}
查看xml文件:
parallel="methods":
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="methods" thread-count="3">
<!--
parallel="methods"蚌铜,methods級別锨侯,所有用例都可以在不同的線程中執(zhí)行
thread-count:代表了最大并發(fā)線程數(shù)
xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
-->
<test name="testthread">
<classes>
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
</classes>
</test>
</suite>
執(zhí)行結(jié)果為:
parallel="classes":
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="classes" thread-count="3">
<!--
parallel="methods"冬殃,methods級別囚痴,所有用例都可以在不同的線程中執(zhí)行
thread-count:代表了最大并發(fā)線程數(shù)
xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
parallel="classes"审葬,classs級別:
相同的class tag 下的用例在同一個線程中執(zhí)行
不同的class tag 下的用例可以在不同的線程中執(zhí)行
-->
<test name="testthread">
<classes>
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
</classes>
</test>
</suite>
測試結(jié)果:
創(chuàng)建不同的classs:
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="classes" thread-count="6" >
<!--
parallel="methods"深滚,methods級別,所有用例都可以在不同的線程中執(zhí)行
thread-count:代表了最大并發(fā)線程數(shù)
xml文件配置這種方式不能指定線程池涣觉,只有方法上才可以指定線程池
parallel="classes"痴荐,classs級別:
相同的class tag 下的用例在同一個線程中執(zhí)行
不同的class tag 下的用例可以在不同的線程中執(zhí)行
-->
<test name="testthread">
<classes name="demo1">
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
</classes>
<classes name="demo2">
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
</classes>
</test>
</suite>
測試結(jié)果,線程會有些亂:
parallel="tests",tests級別:
相同的test tag 下的用例在同一個線程中執(zhí)行
不同的test tag 下的用例可以在不同的線程中執(zhí)行
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="tests" thread-count="3" >
<!--
parallel="methods"官册,methods級別生兆,所有用例都可以在不同的線程中執(zhí)行
thread-count:代表了最大并發(fā)線程數(shù)
xml文件配置這種方式不能指定線程池,只有方法上才可以指定線程池
parallel="classes"膝宁,classs級別:
相同的class tag 下的用例在同一個線程中執(zhí)行
不同的class tag 下的用例可以在不同的線程中執(zhí)行
parallel="tests"鸦难,tests級別:
相同的test tag 下的用例在同一個線程中執(zhí)行
不同的test tag 下的用例可以在不同的線程中執(zhí)行
-->
<test name="testthread">
<classes name="demo1">
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
</classes>
</test>
</suite>
設(shè)置不同的tests:
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="thread" parallel="tests" thread-count="3" >
<!--
parallel="methods"根吁,methods級別,所有用例都可以在不同的線程中執(zhí)行
thread-count:代表了最大并發(fā)線程數(shù)
xml文件配置這種方式不能指定線程池合蔽,只有方法上才可以指定線程池
parallel="classes"击敌,classs級別:
相同的class tag 下的用例在同一個線程中執(zhí)行
不同的class tag 下的用例可以在不同的線程中執(zhí)行
parallel="tests",tests級別:
相同的test tag 下的用例在同一個線程中執(zhí)行
不同的test tag 下的用例可以在不同的線程中執(zhí)行
-->
<test name="testthread">
<classes name="demo1">
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXml"/>
</classes>
</test>
<test name="testthread2">
<classes name="demo2">
<class name="com.course.LearnTestNgCases.MultiThread.MultiThreadOnXmltwo"/>
</classes>
</test>
</suite>
測試結(jié)果:
總結(jié):正常情況下辈末,用methods級別會比較多愚争,多條用例在多個線程下執(zhí)行,會節(jié)省測試時間挤聘。
14轰枝、超時測試
@Test(timeOut = 3000)//超時等待3000毫秒,就是3秒
package com.course.LearnTestNgCases;
import org.testng.annotations.Test;
/*
超時測試组去,測試中調(diào)接口可能會超時鞍陨,導(dǎo)致調(diào)接口失敗,通過設(shè)置超時時間从隆,使用例執(zhí)行通過诚撵。
*/
public class TimeOutTest {
@Test(timeOut = 3000)//超時等待3000毫秒,就是3秒
public void test1() throws InterruptedException {
//設(shè)置一個線程键闺,讓線程休眠2000毫秒,等待時間大于休眠時間寿烟,所以用例會執(zhí)行成功
Thread.sleep(2000);
}
@Test(timeOut = 3000)
public void test2() throws InterruptedException {
//設(shè)置一個線程,讓線程休眠4000毫秒辛燥,等待時間小于休眠時間筛武,所以用例會執(zhí)行失敗
Thread.sleep(4000);
}
}
注:更多的超時設(shè)置可以參考:
摘抄自:https://blog.csdn.net/u011191463/article/details/78664896