一. 自动化测试框架

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/login

2.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接口自动化测试框架相关推荐

  1. 手把手教你搭建java接口自动化测试框架(四):断言、生成测试报告

    手把手教你搭建java接口自动化测试框架(四):断言.生成测试报告 上一集说到post和Get请求,请求后得到的响应(即接口返回值)是我们想要的吗 比如网站上get接口文档说明 : "dat ...

  2. Java接口自动化测试框架学习

    Java接口自动化测试框架学习 高级软件测试,性能测试,功能测试,自动化测试,接口测试,移动端测试,手机测试,WEB测试,渗透测试,测试用例设计,黑盒测试,白盒测试,UFT高级测试,Android测试 ...

  3. java接口自动化Excel占位符_基于maven+java+TestNG+httpclient+poi+jsonpath+ExtentReport的接口自动化测试框架...

    接口自动化框架 项目说明 本框架是一套基于maven+java+TestNG+httpclient+poi+jsonpath+ExtentReport而设计的数据驱动接口自动化测试框架,TestNG ...

  4. jmeter的java测试框架_性能测试学习之路 (四)jmeter 脚本开发实战(JDBC JMS 接口脚本 轻量级接口自动化测试框架)...

    1.业务级脚本开发 登录脚本->思路:在线程组下新建两个HTTP请求,一个是完成访问登录页,一个是完成登录的数据提交. 步骤如下: 1) 访问登录页 2) 提交登录数据的HTTP PS:对于业务 ...

  5. python接口自动化测试框架实战从设计到开发_【B0753】[java视频教程]Python接口自动化测试框架设计到开发完整版视频教程 it教程...

    Java视频教程名称:Python接口自动化测试框架设计到开发完整版视频教程   java自学网[javazx.com]  Python视频教程   it教程 Java自学网收集整理 java论坛&q ...

  6. python+requests接口自动化测试框架实例详解教程(米兔888)

    来源:https://my.oschina.net/u/3041656/blog/820023 源码:https://pan.baidu.com/s/1lgIEToiczTvvjy--p-N20g 提 ...

  7. python3接口自动化测试_【python3+request】python3+requests接口自动化测试框架实例详解教程...

    前段时间由于公司测试方向的转型,由原来的web页面功能测试转变成接口测试,之前大多都是手工进行,利用postman和jmeter进行的接口测试,后来,组内有人讲原先web自动化的测试框架移驾成接口的自 ...

  8. python接口自动化测试框架实战从设计到开发_Python接口自动化测试框架实战 从设计到开发...

    第1章 课程介绍(不要错过) 本章主要讲解课程的详细安排.课程学习要求.课程面向用户等,让大家很直观的对课程有整体认知! 第2章 接口测试工具Fiddler的运用 本章重点讲解如何抓app\web的h ...

  9. 2022超级好用的接口自动化测试框架:基于python+requests+pytest+allure实现

    众所周知,目前市面上大部分的企业实施接口自动化最常用的有两种方式: 1.基于工具类的接口自动化,如: Postman+Newman+Jenkins+Git/svn Jmeter+Ant+Jenkins ...

  10. 用robot framework + python实现http接口自动化测试框架

    前言 下周即将展开一个http接口测试的需求,刚刚完成的java类接口测试工作中,由于之前犯懒,没有提前搭建好自动化回归测试框架,以至于后期rd每修改一个bug,经常导致之前没有问题的case又产生了 ...

最新文章

  1. 关于ARM启动的一篇文章
  2. php 彩色标签插件,zblog php 彩色标签CSS源码
  3. 利用 exe4j 将 JAR 包制成免安装 JDK 的 EXE 文件
  4. 用python画常密度轮廓线,如何使用Matplotlib在极坐标中绘制具有等高线密度线的散点图?...
  5. return两个返回值_异步函数的两个视角
  6. 开源软件free download manager在windows defender中报毒
  7. 怎么看电脑是不是linux系统,怎么查看自己的电脑系统是什么版本
  8. 数据--第40课 - 图的定义
  9. 使用 Python 进行人脸识别(第2部分)
  10. 在Ubuntu下安装Visual Studio Code
  11. Spring-几种组件注册方式
  12. 挡不住,逃不过,还是阳了
  13. 点乘a*b和叉乘aXb
  14. relay_log_purge参数一则
  15. AUC和ROC曲线的前世今生
  16. 内网穿透工具--NATAPP讲解
  17. Windows Server 2012 R2安装域控制器完整版
  18. MySQL数据库学习笔记(一)—— 基础概念
  19. 多视角人脸正面化生成综述:Multi-view Frontal Face Image Generation: A Survey
  20. pgn model和一些解决oov的方法

热门文章

  1. 4.7UF400V 10*8.4贴片铝电解电容封装尺寸
  2. 车联网网络安全技术研究
  3. 1_Hadoop安装部署及常用配置(HDFS+YARN)
  4. 小米路由器3 变砖 ttl 救砖,刷入padavan
  5. 盘点(腾讯字节谷歌等大厂)面试中常见的智力题
  6. scrollTop、clientHeight、 scrollHeight...学完真的理解了
  7. PMP考试真题模拟PMP考试模拟试题及答案详解
  8. OSDI 2022 Roller 论文解读
  9. T电脑经典基础知识技术OC
  10. 全国30省市分地区能源结构(煤炭占比)2003-2019年