1.需求分析:
为了减轻测试人员对被测系统页面的检查,让更多的测试时间花费在业务流程以及功能测试点上,所以需要一个自动检测网站页面错误(死链接,静态资源报错等)的功能,降低测试工作范围从而提高测试效率。
具体需求:
1.定时自动检测服务器状态
2.发生错误邮件通知
3.生成测试报告
2.技术选型:
1.使用Selenium自动化测试框架实现。
优点:能获取访问页面静态资源或JS报错等页面错误,兼容性较强。
缺点:对测试环境依赖性较大,(浏览器版本、Jar版本、浏览器驱动)、脚本不稳定并且执行速度过慢。
2.使用HttpClient工具包实现。
优点:执行速度快,对测试环境要求较低
缺点:兼容性差,各项目访问页面存在加密并且加密方式不同,难以做到大规模使用。
结合现状,选择Selenium作为实现工具。
3.方案设计
1.通过Selenium打开网页并进行简单爬虫获取子页面链接。
2.页面报错时Selenium能获取到相关错误并获取。
3.通过递归获取到指定层级所有子页面树并依次测试
4.当发生错误时邮件通知指定测试人员
5.生成测试报告
项目结构
![](https://img.haomeiwen.com/i4207456/e6783d8509850736.png)
4.核心代码
1.配置![](https://img.haomeiwen.com/i4207456/c1318d5cb2a72a1b.png)
![](https://img.haomeiwen.com/i4207456/2c7fc3f46adf36d1.png)
![](https://img.haomeiwen.com/i4207456/a01635a3eeac7a37.png)
![](https://img.haomeiwen.com/i4207456/f5d65bb8bb57796a.png)
5.项目(执行引擎)
public class Project {
private Config config;
private List<TestCase> testCases;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Config getConfig() {
return config;
}
public void setConfig(Config config) {
this.config = config;
}
public List<TestCase> getTestCases() {
return testCases;
}
public void setTestCases(List<TestCase> testCases) {
this.testCases = testCases;
}
/**
* 测试执行
* @return
* @throws Exception
*/
public List<Result> run() throws Exception {
List<Result> results = new ArrayList<Result>();
if (testCases != null && testCases.size() > 0) {
//循环项目中的所有测试用例
for (TestCase testCase : testCases) {
results.addAll(testRun(testCase));
}
int t = 0, s = 0, skip = 0, f = 0;
for (Result result : results) {
t++;
switch (result.getStatus()) {
case failure:
f++;
break;
case skip:
skip++;
break;
case success:
s++;
break;
default:
break;
}
}
if (t > s) {//如果该项目测试结果总数>成功的数量则发送邮件
try {
new MailSender(config.getReceivers(), "米多测试报告", "自动检测服务器状态");
} catch (Exception e) {
}
}
}
return results;
}
/**
* 测试用例执行并返回该测试 用例所有的测试结果
*
* @param testCase
* @return
*/
public List<Result> testRun(TestCase testCase) {
WebDriver driver = null;
List<Result> results = new ArrayList<>();
String[] browsers = config.getBrowsers();
String[] environments = testCase.getEnvironments();
String login = testCase.getLogin();
String title = testCase.getTitle();
// 循环浏览器设置
for (String browser : browsers) {
driver = BrowserUtil.init(browser);
// 循环测试用例指定的测试环境
for (String environment : environments) {
EnvironmentConfig environmentConfig = config.getEnvironment(environment);
Result result = new Result();
result.setProjectName(this.name);
result.setTestCaseTitle(title);
result.setBrowser(browser);
result.setEnvironment(environment);
if (environmentConfig == null) {// 如果测试用例中指定的环境配置找不到则该用例对该环境的测试结果为failure
result.setStatus(Status.failure);
result.setImg(BrowserUtil.screenshotByBase64(driver));
result.setErrors(Arrays.asList(new String[] { "环境配置错误,找不到:" + environment + "环境" }));
results.add(result);
continue;
} else {
if (login != null && login.trim().length() > 0) {// 判断测试用例是否需要登录
if (!environmentConfig.login(driver, login)) {// 判断登录是否报错,如果报错了,则截图并将该用例对该环境的状态为跳过
result.setStatus(Status.skip);
result.setErrors(Arrays.asList(new String[] { "登陆失败" }));
result.setImg(BrowserUtil.screenshotByBase64(driver));
results.add(result);
continue;
}
}
String url = environmentConfig.getHost() + testCase.getAddress();
int level = testCase.getLevel();
int interval = testCase.getInterval();
Set<String> requested = new HashSet<>();
//执行测试操作
Map<Result, Object> map = run(requested, driver, url, level, interval, browser, environment, title);
results.addAll(getResults(map));
}
}
driver.quit();
}
return results;
}
/**
* 单用例执行
*
* @param requested
* @param driver
* @param url
* @param level
* @param interval
* @param browser
* @param environment
* @param testCaseTitle
* @return
*/
private Map<Result, Object> run(Set<String> requested, WebDriver driver, String url, int level, int interval,
String browser, String environment, String testCaseTitle) {
Map<Result, Object> map = new LinkedHashMap<>();
driver.get(url);
requested.add(url.toLowerCase());
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
}
Result result = getResult(driver, this.name, browser, environment, url, testCaseTitle);
if (level > 1) { // 如果传入的level>1则获取此页面所有子页面
List<String> sub = getSubPage(driver, requested, interval);
if (sub.size() == 0) {// 如果子页面数量为0。则不操作
map.put(result, null);
} else if (sub.size() == 1 && !requested.contains(sub.get(0).toLowerCase())) {// 如果子页面数量为1并且没被访问过则递归测试并将结果保存为Map
requested.add(sub.get(0).toLowerCase());
map.put(result,
run(requested, driver, sub.get(0), level - 1, interval, browser, environment, testCaseTitle));
} else {// 如果子页面数量>1则递归测试所有子页面并且将测试结果保存为List
ArrayList<Object> list = new ArrayList<>();
for (String string : sub) {
if (!requested.contains(string.toLowerCase())) {
requested.add(string.toLowerCase());
list.add(run(requested, driver, string, level - 1, interval, browser, environment,
testCaseTitle));
}
}
map.put(result, list);
}
} else {
map.put(result, null);
}
return map;
}
/**
* 获取当前页面所有子页面
*
* @param driver
* @param requested
* @param interval
* @return
*/
private List<String> getSubPage(WebDriver driver, Set<String> requested, int interval) {
List<String> subPage = new ArrayList<>();
// 获取所有<a>标签的所有href属性值。
List<WebElement> elements = driver.findElements(By.tagName("a"));
for (WebElement webElement : elements) {
String url = webElement.getAttribute("href");
// 排除不存在href并且href为JS的链接
if (url != null && url.contains("/")) {
// 如果当前链接是相对链接,则获取当前链接的Host进行拼接
if (url.startsWith("/")) {
String nowUrl = driver.getCurrentUrl();
url = nowUrl.substring(0, nowUrl.indexOf("/", nowUrl.indexOf("//") + 2)) + url;
}
// 为防止误操作(访问了删除类型接口),所以将链接参数去除
if (url.contains("?")) {
url = url.substring(0, url.indexOf("?"));
}
// 经过处理后的链接如果不存在已经访问过的链接中,则将此链接加入到子页面列表中
if (!requested.contains(url)) {
subPage.add(url);
}
}
}
return subPage;
}
/**
* 页面测试结果
*
* @param driver
* @param projectName
* @param browser
* @param environment
* @param requestUrl
* @param testCaseTitle
* @return
*/
private Result getResult(WebDriver driver, String projectName, String browser, String environment,
String requestUrl, String testCaseTitle) {
List<String> errors = new ArrayList<>();
// 获取当前页面所有报错信息
for (String log : driver.manage().logs().getAvailableLogTypes()) {
for (LogEntry entry : driver.manage().logs().get(log)) {
if (entry.getLevel() == Level.SEVERE) {
errors.add(entry.getMessage());
}
}
}
Result result = new Result();
result.setProjectName(projectName);
result.setBrowser(browser);
result.setEnvironment(environment);
result.setRequestUrl(requestUrl);
result.setTestCaseTitle(testCaseTitle);
result.setPageUrl(driver.getCurrentUrl());
// 如果存在报错,则测试结果状态为failure并且截图
if (errors.size() > 0) {
result.setStatus(Status.failure);
result.setErrors(errors);
result.setImg(BrowserUtil.screenshotByBase64(driver));
} else {
result.setStatus(Status.success);
errors.add(requestUrl + " - success");
result.setErrors(errors);
}
return result;
}
/**
* 递归获取所有测试结果
*
* @param object
* @return
*/
private List<Result> getResults(Object object) {
List<Result> results = new ArrayList<>();
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
for (Object result : map.keySet()) {
results.add((Result) result);
results.addAll(getResults(map.get(result)));
}
} else if (object instanceof List) {
for (Object result : (List<?>) object) {
results.addAll(getResults(result));
}
} else if (object instanceof Result) {
results.add((Result) object);
}
return results;
}
}
6.主方法
![](https://img.haomeiwen.com/i4207456/c9134a99a4597d0d.png)
- XML数据源
buid.xml
<?xml version="1.0" encoding="UTF-8"?>
<projects>
<project path='//projects//lipinshichangConfig.xml' />
<project path='//projects//BgConfig.xml' />
</projects>
project.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name='bg'>
<config>
<browser value='chrome' />
<e-mail>
<receiver name='' value='546724963@qq.com' />
</e-mail>
<environments>
<environment name='138' host='**********保密***********'>
<logins>
<login name='account' url='**********保密***********' check=''>
<operation locator='id.uName' type='input' value='userName' />
<operation locator='id.pwd' type='input' value='password' />
<operation locator='xpath.//input[@type="button"]' type='click' />
</login>
</logins>
</environment>
</environments>
</config>
<testPlan>
<testcase title='Bg管理后台' address='**********保密***********' level='1'
login='account' environments='138' interval='500' />
</testPlan>
</project>
-
测试报告展示
![](https://img.haomeiwen.com/i4207456/06207586a59f667b.png)
网友评论