Java接口自动化测试框架
生活随笔
收集整理的這篇文章主要介紹了
Java接口自动化测试框架
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一. 自動化測試框架
1. 測試框架TestNG
1.1 適合測試人員使用的原因
(1)比Junit涵蓋功能更全面的測試框架
(2)Junit更適合隔離性比較強的單元測試
(3)TestNG更適合復雜的集成測試
1.2 基本介紹
(1)基本注解:決定執行順序
例:
@Test:標記一個類或方法作為測試的一部分
@beforeTest、@afterTest:做前置或后置處理
(2)屬性
例:
groups:分組測試
dependsOnGroups:依賴測試
description:描述
(3)測試套件
組織測試類一起執行的或者一組行為的測試用例的集合,由一個XML文件標記
2. 測試報告ExtentReport
2.1 添加測試類
ExtentTestNGIReporterListener
2.2 基本配置
在測試套件中 @listener標簽下添加監聽器
3. HttpClient
一個HTTP客戶端編程工具,可用來發送請求、接收響應
4. MyBatis
持久層框架,支持定制化 SQL、存儲過程以及高級映射。
可以使用簡單的 XML 或注解來配置和映射原生信息。
5. MySQL
存儲測試用例
二. 編寫步驟及文件目錄結構
1. 測試用例的表結構設計
2. 基礎配置文件設計
2.1 pom.xml:引入第三方依賴包
配置httpclient、json、mybatis、mysql、lombok、extentreports、testng的各種依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>AutoTest</artifactId><groupId>Chapter</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>Chapter12</artifactId><dependencies><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>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.10</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.0.4</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies> </project>2.2 databaseConfig.xml:數據庫配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//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.加載數據庫驅動 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><!-- 2.數據庫連接地址 --><property name="url" value="jdbc:mysql://localhost:3306/course?serverTimezone=GMT"/><!-- 數據庫用戶... --><property name="username" value="root"/><!-- 數據庫密碼... --><property name="password" value="12345678"/></dataSource></environment></environments><!-- 注冊映射文件:java對象與數據庫之間的xml文件路徑! --><mappers><mapper resource="mapper/SQLMapper.xml"/></mappers> </configuration>2.3 application.properties:接口信息配置文件
test.url=http://localhost:8080#登陸接口uri login.uri=/v1/login2.4 testng.xml:用以執行所有testng的測試套件
<?xml version="1.0" encoding="UTF-8" ?> <suite name="用戶管理系統測試套件"><test name="用戶管理系統測試用例"><classes><class name="com.tester.cases.LoginTest"><methods><include name="loginTrue"/><include name="loginFalse"/></methods></class> </classes></test><listeners><listener class-name="com.tester.config.ExtentTestNGIReporterListener"/></listeners> </suite>2.5 SQLMapper.xml:用以存儲所有測試用例的SQL語句
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.tester.model"><!--獲取登陸接口case--><select id="loginCase" parameterType="Integer" resultType="com.tester.model.LoginCase">select *from logincase where id=#{id};</select></mapper>3. model層、config層、utils層、cases層
3.1 model層:放置各個接口的數據配置文件+InterfaceName枚舉
3.1.1 放置登錄接口的數據配置文件LoginCase.java
package com.tester.model;import lombok.Data;@Data public class LoginCase {private int id;private String userName;private String password;private String expected; }3.1.2 InterfaceName.java
package com.tester.model;public enum InterfaceName {LOGIN }3.2 Config層:配置信息TestConfig類+ExtentTestNGReportListener類
3.2.1TestConfig類:聲明各個測試用例的URL、和之后要用的一些全局變量
package com.tester.config;import lombok.Data; import org.apache.http.client.CookieStore; import org.apache.http.impl.client.DefaultHttpClient;@Data public class TestConfig {//登陸接口uripublic static String loginUrl;//聲明http客戶端public static DefaultHttpClient defaultHttpClient;//用來存儲cookies信息的變量public static CookieStore store; }3.2.2 ExtentTestNGReportListener類:測試報告配置
package com.tester.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;@Overridepublic 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;}//統計suite下的成功、失敗、跳過的總用例數int suiteFailSize=0;int suitePassSize=0;int suiteSkipSize=0;ExtentTest suiteTest=null;//存在多個suite的情況下,在報告中將同一個一個suite的測試結果歸為一類,創建一級節點。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){//沒有創建suite的情況下,將在SuiteResult的創建為一級節點,否則創建為suite的一個子節點。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());//統計SuiteResult下的數據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() {//文件夾不存在的話進行創建File reportDir= new File(OUTPUT_FOLDER);if(!reportDir.exists()&& !reportDir .isDirectory()){reportDir.mkdir();}ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);// 設置靜態文件的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) {//存在父節點時,獲取父節點的標簽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) {//調整用例排序,按時間排序Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {@Overridepublic 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="";//如果有參數,則使用參數的toString組合代替報告中的namefor(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{//作為子節點進行創建時,設置同父節點的標簽一致,便于報告檢索。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();} }3.3 utils層: 抽取公用的方法ConfigFile類+DatabaseUtil類
3.3.1 ConfigFile類:對各個測試用例的URL進行賦值
package com.tester.utils;import com.tester.model.InterfaceName;import java.util.Locale; import java.util.ResourceBundle;public class ConfigFile {public static ResourceBundle bundle=ResourceBundle.getBundle("application", Locale.CHINA);public static String getUrl(InterfaceName name){String address=bundle.getString("test.url");String uri="";String testUrl;if(name==InterfaceName.LOGIN){uri=bundle.getString("login.uri");}testUrl=address+uri;return testUrl;} }3.3.2 DatabaseUtil類:配置一個getSqlSession()方法
作用是執行配置文件SQLMapper中的SQL語句
package com.tester.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;public class DatabaseUtil {public static SqlSession getSqlSession() throws IOException {//獲取配置的資源文件Reader reader= Resources.getResourceAsReader("databaseConfig.xml");//得到SqlSessionFactory,使用類加載器加載xml文件SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(reader);//得到sqlsession對象,這個對象就能執行配置文件中的sql語句啦SqlSession session=factory.openSession();return session;} }3.4 cases層:用來放接口的測試用例
package com.tester.cases;import com.tester.config.TestConfig; import com.tester.model.InterfaceName; import com.tester.model.LoginCase; import com.tester.utils.ConfigFile; import com.tester.utils.DatabaseUtil; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.apache.ibatis.session.SqlSession; import org.json.JSONObject; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test;import java.io.IOException;public class LoginTest {@BeforeTest(groups = "loginTrue",description = "測試準備工作,獲取HttpClient對象")public void beforeTest(){TestConfig.getUserInfoUrl= ConfigFile.getUrl(InterfaceName.GETUSERINFO);TestConfig.getUserListUrl=ConfigFile.getUrl(InterfaceName.GETUSERLIST);TestConfig.addUserUrl=ConfigFile.getUrl(InterfaceName.ADDUSERINFO);TestConfig.loginUrl=ConfigFile.getUrl(InterfaceName.LOGIN);TestConfig.updateUserInfoUrl=ConfigFile.getUrl(InterfaceName.UPDATEUSERINFO);TestConfig.defaultHttpClient=new DefaultHttpClient();}@Test(groups = "loginTrue",description = "用戶成功登陸接口")public void loginTrue() throws IOException {SqlSession session= DatabaseUtil.getSqlSession();LoginCase loginCase=session.selectOne("loginCase",1);System.out.println(loginCase.toString());System.out.println(TestConfig.loginUrl);//下邊的代碼為寫完接口的測試代碼String result=getResult(loginCase);//處理結果,就是判斷返回結果是否符合預期Assert.assertEquals(loginCase.getExpected(),result);}@Test(groups = "loginFalse",description = "用戶登錄接口失敗")public void loginFalse() throws IOException {SqlSession session=DatabaseUtil.getSqlSession();LoginCase loginCase=session.selectOne("loginCase",2);System.out.println(loginCase.toString());System.out.println(TestConfig.loginUrl);//下邊的代碼為寫完接口的測試代碼String result=getResult(loginCase);//處理結果,就是判斷返回結果是否符合預期Assert.assertEquals(loginCase.getExpected(),result);}private String getResult(LoginCase loginCase) throws IOException {//下邊的代碼為寫完接口的測試代碼HttpPost post=new HttpPost(TestConfig.loginUrl);JSONObject param=new JSONObject();param.put("userName",loginCase.getUserName());param.put("password",loginCase.getPassword());//設置請求頭信息,設置headerpost.setHeader("content-type","application/json");//將參數信息添加到方法中StringEntity entity=new StringEntity(param.toString(),"utf-8");post.setEntity(entity);//聲明一個對象來進行響應結果的存儲String result;//執行post方法HttpResponse response=TestConfig.defaultHttpClient.execute(post);//獲取響應結果result= EntityUtils.toString(response.getEntity(),"utf-8");System.out.println(result);TestConfig.store=TestConfig.defaultHttpClient.getCookieStore();return result;} }總結
以上是生活随笔為你收集整理的Java接口自动化测试框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2018年前端年度工作总结
- 下一篇: html 文本横竖切换,(横竖屏切换/强