一.创建一个springBoot项目导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!--支持jsp -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 解决访问页面404 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
配置application.properties配置文件
#JDBC连接本地mysql
spring.datasource.url=jdbc:mysql://192.168.25.125:3306/casemanage?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#配置springMVC视图解析器的文件后缀是jsp(解析后缀为jsp的页面)
spring.mvc.view.suffix=.jsp
#mybatis配置 #指定xml映射文件的路径
mybatis.mapper-locations=classpath:mappers/*.xml
server.port=808
因为一开始就在数据库中创建了表和数据,现在就直接进入实现代码
1.1项目结构
实例的思路:
主要分为6个表单
一、测试系统信息表
表名 APP_INFO
列名 数据类型(精度范围) 空/非空 说明
APP_ID int N/PK
APP_NAME varchar(256) N 测试系统名称
APP_DESC varchar(1024) Y 描述
ACCESS_PATH varchar(1024) N 测试系统访问路径
二、测试用例信息表
表名 TEST_CASE
列名 数据类型(精度范围) 空/非空 说明
CASE_ID int N/PK 节点id
APP_ID int N/FK 所属测试系统id
CASE_NAME varchar(256) N 测试用例名称
CASE_DESC Varchar(1024) Y 描述
CASE_CATEGORY char(1) N 分类:自动或人工
CASE_ ASSERT varchar (2048) Y 流程测试预期结果
三、用例节点信息表
表名 CASE_NODE
列名 数据类型(精度范围) 空/非空 说明
NODE_ID int N/PK
CASE_ID int N
NODE_SEQUENCE int Y 执行序号
NODE_NAME varchar(256) N 节点描述
NODE_DESC varchar(1024) Y 描述
NODE_ACCESS_PATH varchar (2048) Y 用例节点访问路径
INPUT _ELEMENT json N 节点测试时,所有需要输入元素的css、value等信息
NODE_ASSERT varchar (1024) Y 用例节点断言
四、测试计划表
表名 TEST_PLAN
列名 数据类型(精度范围) 空/非空 说明
PLAN_ID int N/PK
APP_ID int N/FK
PLAN_NAME varchar(256) N 计划名称
PLAN_DESC varchar(1024) Y 描述
CREATOR varchar(32) Y 创建人
CREATOR_TIME date Y 创建时间
五、测试计划用例表
表名 TEST_PLAN_CASE
列名 数据类型(精度范围) 空/非空 说明
PLAN_ID int N/PK 计划主键id
CASE_ID int N 用例主键id
TEST_PLAN_CASE _ID int N/PK Case表Id
六、测试计划用例结果表
表名 TEST_PLAN_CASE_RESULT
列名 数据类型(精度范围) 空/非空 说明
PLAN_ID int N/PK
CASE_ID varchar(256) N 用例主键id
TEST_STATUS char(1) N 结果 :1通过、2未通过
TEST_RESULT varchar(2048) 执行结果
TEST_RESULT_SCREENING_PATH varchar(1024) Y 截图存放地址 d多个截图怎么办
CREATOR varcahr(32) Y 测试人
CREATE_TIME date N 测试时间
一个系统关联多个测试计划一对多关联,一个测试计划关联多个测试测试用例信息,一个测试用例信息关联多个测试节点信息
最后把测试用例的节点输出的结果存入到结果表中,断言使用URL断言方式和异常截图断言方式.
Controller业务层
下面controller是主要的逻辑层通过测试用例信息表的节点id查询出对应的用例节点信息表数据通过执行序号字段排序通过驱动来解析INPUT _ELEMENT字段存储的所有节点信息进行对项目自动化测试的一系列操作其中自动化测试完成后执行 testPlanCaseResultService.insert(testPlanCaseResult);方法,进行一个测试结果的插入(testPlanCaseResultService的方法就不放出来了就是一个普通的插入方法)
package com.iris.autotest.at.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iris.autotest.at.entity.CaseNode;
import com.iris.autotest.at.entity.TestPlanCaseResult;
import com.iris.autotest.at.service.CaseNodeService;
import com.iris.autotest.at.service.TestPlanCaseResultService;
import com.iris.autotest.at.util.BrowserUtil;
import com.iris.autotest.at.util.CaseUtil;
import org.openqa.selenium.WebDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Controller
public class AppInFoController {
@Autowired
public TestPlanCaseResultService testPlanCaseResultService;
@Autowired
public CaseNodeService caseNodeService;
//根据测试用例信息id查询所有有关的用例节点信息集合
@RequestMapping("/appInFo1")
public String queryList(HttpServletRequest request){
Exception exception = null;
int caseId =Integer.parseInt(request.getParameter("id"));
WebDriver driver = BrowserUtil.getDriver();
String testStatus = "";
List<CaseNode> caseNodeList = caseNodeService.selectAllId(caseId);
for (CaseNode caseNode : caseNodeList) {
String testResult = "";
//caseNodeList存储的是所有所有所属这个测试用例信息的节点信息表内容
String inputElement = caseNode.getInputElement();
//inputElement所有节点信息
String nodeName = caseNode.getNodeName();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if ("newProject".equals(nodeName) || "edit".equals(nodeName)) {
inputElement = inputElement.replaceAll("项目名称", "项目名称" + sf.format(new Date()));
}
Map map = (Map) JSONObject.parse(inputElement);
//nodeName的名字等于json的key值根据nodeName查询单个节点的节点信息
JSONArray array = (JSONArray) map.get(nodeName);
String resultUrl = CaseUtil.testDriver(array, driver);
if (resultUrl.startsWith(caseNode.getNodeAssert())) {
testResult += caseNode.getNodeName() + " : success;";
testStatus = "1";
} else {
testResult += caseNode.getNodeName() + " : fail";
testStatus = "2";
}
String testResultScreeningPath = "";
String creator = "";
TestPlanCaseResult testPlanCaseResult = new TestPlanCaseResult();//执行结果
testPlanCaseResult.setTestStatus(testStatus);
testPlanCaseResult.setCaseId(caseId);
testPlanCaseResult.setTestResultScreeningPath(testResultScreeningPath);
testPlanCaseResult.setCreator(creator);
testPlanCaseResult.setTestResult(testResult);
testPlanCaseResultService.insert(testPlanCaseResult);
request.setAttribute("caseNodeList", caseNodeList);
}
return "/TestCase";
}
}
selenium的浏览器驱动类
package com.iris.autotest.at.util;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.concurrent.TimeUnit;
public class BrowserUtil {
public static WebDriver driver;
public static WebDriver getDriver () {
System.setProperty("webdriver.chrome.driver",
"C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
return driver;
}
}
package com.iris.autotest.at.util;
import com.alibaba.fastjson.JSONArray;
import com.iris.autotest.at.entity.Locator;
import com.iris.autotest.at.entity.Page;
import org.openqa.selenium.WebDriver;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
查询出来的INPUT _ELEMENT在这个工具类进行解析,下面的Locator类Page类UIExecutor接口和UIExecutorImpl实现类主要是对selenium方法进行封装
//selenium方法解析工具类
public class CaseUtil {
public static String testDriver(JSONArray jsonArray, WebDriver driver) {
List<Locator> locatorList = new ArrayList<Locator>();
if (jsonArray != null) {
for (int i = 0; i < jsonArray.size(); i++) {
Map m = (Map)jsonArray.get(i);
Locator locator = new Locator(m.get("locatorKey").toString(), m.get("locatorType").toString(),
m.get("compareType").toString(), m.get("inputValue").toString());
locatorList.add(locator);
}
}
Page page = new Page(driver, locatorList);
for (Locator locator : page.getLocatorList()) {
switch (locator.getCompareType()) {
case "input":
page.sendKeys(locator);
break;
case "click":
page.click(locator);
break;
case "switchWindow":
page.switchWindow(locator.getValueKey());
break;
case "switchIframe":
page.switchIframe(locator);
break;
case "get":
page.getWindow(locator.getValueKey());
break;
case "select":
page.select(locator);
break;
case "wait":
page.waitElement(locator);
break;
case "file":
page.click(locator);
try {
Runtime.getRuntime().exec(locator.getValueKey());
} catch (IOException e) {
e.printStackTrace();
}
break;
case "alert":
page.switchAlert();
break;
}
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return driver.getCurrentUrl();
}
}
package com.iris.autotest.at.entity;
import com.iris.autotest.at.util.UIExecutorImpl;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import java.util.List;
public class Page extends UIExecutorImpl {
protected WebDriver driver;
protected List<Locator> locatorList;
public void select(Locator locator) {
WebElement element = getElement(locator);
Select select = new Select(element);
select.selectByValue(locator.getValueKey());
}
public Page(WebDriver driver, List<Locator> locatorList){
super(driver);
this.locatorList = locatorList;
}
public List<Locator> getLocatorList() {
return locatorList;
}
public void setLocatorList(List<Locator> locatorList) {
this.locatorList = locatorList;
}
}
package com.iris.autotest.at.entity;
public class Locator {
private String locatorKey;
private String locatorType;
private String compareType;
private String valueKey;
public Locator(String locatorKey, String locatorType, String compareType, String valueKey){
this.locatorKey = locatorKey;
this.locatorType = locatorType;
this.compareType = compareType;
this.valueKey = valueKey;
}
public String getValueKey() { return valueKey; }
public String getLocatorKey() {
return locatorKey;
}
public String getLocatorType() {
return locatorType;
}
public String getCompareType() {
return compareType;
}
}
package com.iris.autotest.at.util;
import com.iris.autotest.at.entity.Locator;
import org.openqa.selenium.WebElement;
public interface UIExecutor {
public void click(Locator locator);
public void sendKeys(Locator locator);
public String getText(Locator locator);
public WebElement getElement(Locator locator) throws Exception;
public boolean isElementDisplayed(Locator locator);
public void switchWindow(String title);
public void switchIframe(Locator locator);
public void waitElement(Locator locator);
public void getWindow(String value);
public void switchAlert();
}
package com.iris.autotest.at.util;
import com.iris.autotest.at.entity.Locator;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
public class UIExecutorImpl implements UIExecutor {
private WebDriver driver;
public UIExecutorImpl(WebDriver driver){
this.driver = driver;
}
public WebDriver getDriver() {
return driver;
}
public void setDriver(WebDriver driver) {
this.driver = driver;
}
@Override
public void click(Locator locator) {
WebElement element = getElement(locator);
element.click();
}
@Override
public void sendKeys(Locator locator) {
WebElement element = getElement(locator);
element.clear();
element.sendKeys(locator.getValueKey());
}
@Override
public String getText(Locator locator) {
WebElement element = getElement(locator);
return element.getText();
}
@Override
public WebElement getElement(Locator locator) {
WebElement webElement = null;
String locatorType = locator.getLocatorType();
String locatorKey = locator.getLocatorKey();
switch (locatorType) {
case "xpath":
webElement = driver.findElement(By.xpath(locatorKey));
break;
case "id":
webElement = driver.findElement(By.id(locatorKey));
break;
case "class":
webElement = driver.findElement(By.className(locatorKey));
break;
case "linkText":
webElement = driver.findElement(By.linkText(locatorKey));
break;
case "name":
webElement = driver.findElement(By.name(locatorKey));
break;
default:
break;
}
return webElement;
}
@Override
public boolean isElementDisplayed(Locator locator) {
WebElement element = getElement(locator);
return element.isDisplayed();
}
@Override
public void switchWindow(String title) {
}
@Override
public void switchIframe(Locator locator) {
driver.switchTo().frame(locator.getLocatorKey());
}
@Override
public void waitElement(Locator locator) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void getWindow(String value) {
driver.get(value);
}
@Override
public void switchAlert() {
driver.switchTo().alert().accept();
}
}
异常处理类,出现异常进行截图操作,方便查看自动化测试过程中的某一个节点出现问题方便查看
package com.iris.autotest.at.handler;
import com.google.common.io.Files;
import com.iris.autotest.at.util.BrowserUtil;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@ControllerAdvice(basePackages = "com.iris.autotest.at.controller")
//异常处理界面 ,当页面出现异常时,截图
public class PageExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView errorHandler() {
File scrFile = ((RemoteWebDriver) BrowserUtil.driver).getScreenshotAs(OutputType.FILE);
try {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String photoPath = "C:/Users/Administrator/Desktop/autoIt/测试异常截图/" + sf.format(new Date()) + ".png";
//利用FileUtils工具类的copy()方法保存getScreenshotAs()返回的文件对象。
//看到网上有使用File.copyFile()方法,我这里测试的结果需要使用copy()方法
Files.copy(scrFile, new File(photoPath));
} catch (IOException e) {
// 异常处理
e.printStackTrace();
}
BrowserUtil.driver.quit();
return new ModelAndView("error");
}
}
其他的就是一些通过查询数据库查询出自动化需要的一些系统页面信息了的逻辑代码了
一.用例节点信息表Dao层
package com.iris.autotest.at.dao;
import com.iris.autotest.at.entity.CaseNode;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@Mapper
public interface CaseNodeDao {
int deleteByPrimaryKey(Integer nodeId);
int insert(CaseNode record);
CaseNode selectByPrimaryKey(Integer nodeId);
List<CaseNode> selectAll();
int updateByPrimaryKey(CaseNode record);
//通过节点id查询节点信息表
List<CaseNode> selectAllId(Integer CaseId);
}
二.service层
package com.iris.autotest.at.service;
import com.iris.autotest.at.entity.Node;
public interface NodeService {
public Node queryNodeByCaseId(int caseId);
}
三.serviceImpl层
package com.iris.autotest.at.service.impl;
import com.iris.autotest.at.dao.NodeDao;
import com.iris.autotest.at.entity.Node;
import com.iris.autotest.at.service.NodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NodeServiceImpl implements NodeService {
@Autowired
public NodeDao nodeDao;
@Override
public Node queryNodeByCaseId(int caseId) {
return nodeDao.queryNodeByCaseId(caseId);
}
}
四.CaseNodeMapper.xml文件
<?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.iris.autotest.at.dao.CaseNodeDao">
<resultMap id="BaseResultMap" type="com.iris.autotest.at.entity.CaseNode">
<id column="NODE_ID" jdbcType="INTEGER" property="nodeId" />
<result column="CASE_ID" jdbcType="INTEGER" property="caseId" />
<result column="NODE_SEQUENCE" jdbcType="INTEGER" property="nodeSequence" />
<result column="NODE_NAME" jdbcType="VARCHAR" property="nodeName" />
<result column="NODE_DESC" jdbcType="VARCHAR" property="nodeDesc" />
<result column="NODE_ACCESS_PATH" jdbcType="VARCHAR" property="nodeAccessPath" />
<result column="INPUT_ELEMENT" jdbcType="VARCHAR" property="inputElement" />
<result column="NODE_ASSERT" jdbcType="VARCHAR" property="nodeAssert" />
</resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from case_node
where NODE_ID = #{nodeId,jdbcType=INTEGER}
</delete>
<select id="selectAll" resultMap="BaseResultMap">
select NODE_ID, CASE_ID, NODE_SEQUENCE, NODE_NAME, NODE_DESC, NODE_ACCESS_PATH, INPUT_ELEMENT,
NODE_ASSERT
from case_node
</select>
<select id="selectCaseNodeByCaseId" resultMap="BaseResultMap" parameterType="java.lang.Integer">
select NODE_ID, CASE_ID, NODE_SEQUENCE, NODE_NAME, NODE_DESC, NODE_ACCESS_PATH, INPUT_ELEMENT,
NODE_ASSERT from case_node where case_id=#{caseId}
</select>
<!--通过CaseId查询对应的节点信息-->
<select id="selectAllId" resultMap="BaseResultMap">
select NODE_ID, CASE_ID, NODE_SEQUENCE, NODE_NAME, NODE_DESC, NODE_ACCESS_PATH, INPUT_ELEMENT,
NODE_ASSERT
from case_node where CASE_ID=#{param1}
</select>
</mapper>