以下是基于java,分别通过HttpClient、Dubbo、Selenium、JdbcTemplate实现Http、RPC、UI、SQL操作的示例代码。导入依赖包后可直接复制代码执行。
代码最大的特点是公共方法入参和出参类型全是String。可直接用于自动化,也可用于准备数据。
一、实现HTTP接口调用
简介
使用HttpClient实现Http接口的调用,可用于接口自动化,也可用于接口测试。
依赖包
代码语言:java复制<!--http接口调用-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!--http接口调用-->
代码实现
代码语言:java复制//1 创建HttpClient客户端
//2 创建Http请求
//3 设置
//4 发送请求
public class HttpCommonClient {
private final List<Header> DEFAULT_HEADER = new ArrayList<>();
private CloseableHttpClient httpClient = null;
private String defaultUrl = null;
/**
* 处理get请求参数,并转换成HttpGet对象
* @param url 完整url(可带参数)
* @param headers 请求头
* @return HttpGet
*/
private HttpGet transformGet(String url, String headers) {
if (!url.startsWith("http")) {
url = this.defaultUrl url;
}
// 创建 HttpGet 请求
HttpGet httpGet = new HttpGet(url);
// 设置请求头
httpGet.setHeaders(this.transformHeaders(headers));
return httpGet;
}
/**
* 处理post请求参数,并转换成HttpGet对象
* @param url 完整url(可带参数)
* @param body 请求体
* @param headers 请求头
* @return HttpPost
*/
private HttpPost transformPost(String url, String body, String headers) {
if (!url.startsWith("http")) {
url = this.defaultUrl url;
}
// 创建 HttpPost 请求
HttpPost httpPost = new HttpPost(url);
httpPost.setHeaders(this.transformHeaders(headers));
if (body != null) {
//设置编码格式避免中文乱码
StringEntity stringEntity = new StringEntity(body, StandardCharsets.UTF_8);
stringEntity.setContentType("application/json");
// 设置 HttpPost 参数
httpPost.setEntity(stringEntity);
}
return httpPost;
}
/**
* 处理delete请求参数,并转换成HttpDelete对象
* @param url 完整url(可带参数)
* @param headers 请求头
* @return HttpDelete
*/
private HttpDelete transformDelete(String url, String headers) {
if (!url.startsWith("http")) {
url = this.defaultUrl url;
}
// 创建 HttpDelete 请求
HttpDelete httpDelete = new HttpDelete(url);
// 设置请求头
httpDelete.setHeaders(this.transformHeaders(headers));
return httpDelete;
}
/**
* 处理put请求参数,并转换成HttpPut对象
* @param url 完整url(可带参数)
* @param body 请求体
* @param headers 请求头
* @return HttpPut
*/
private HttpPut transformPut(String url, String body, String headers) {
if (!url.startsWith("http")) {
url = this.defaultUrl url;
}
// 创建 HttpPut 请求
HttpPut httpPut = new HttpPut(url);
httpPut.setHeaders(this.transformHeaders(headers));
if (body != null) {
//设置编码格式避免中文乱码
StringEntity stringEntity = new StringEntity(body, StandardCharsets.UTF_8);
stringEntity.setContentType("application/json");
// 设置 HttpPost 参数
httpPut.setEntity(stringEntity);
}
return httpPut;
}
private Header[] transformHeaders(String headers) {
List<Header> headerList = new ArrayList<>();
headerList.add(new BasicHeader("Connection", "keep-alive"));
headerList.addAll(DEFAULT_HEADER);
if (headers == null) {
return headerList.toArray(new Header[0]);
}
JSONObject jsonObject = JSON.parseObject(headers);
for (String key : jsonObject.keySet()) {
headerList.add(new BasicHeader(key, jsonObject.get(key).toString()));
}
return headerList.toArray(new Header[0]);
}
/**
* 执行http请求
* @param request 请求对象
* @return 调用结果
*/
private CloseableHttpResponse executeRequest(HttpUriRequest request) throws IOException {
if (httpClient == null) {
httpClient = HttpClients.createDefault();
}
return httpClient.execute(request);
}
/**
* 执行请求并获取结果中的body值
* @param request 请求
* @return response body
*/
private String httpBody(HttpUriRequest request) {
try {
CloseableHttpResponse httpResponse = this.executeRequest(request);
String result = EntityUtils.toString(httpResponse.getEntity());
httpResponse.close();
return result;
} catch (IOException e) {
e.printStackTrace();
return "false";
}
}
/**
* 执行请求并获取结果中的header
* @param request 请求
* @return response body
*/
private String httpHeader(HttpUriRequest request, String headerName) {
try {
CloseableHttpResponse httpResponse = this.executeRequest(request);
Header[] header;
if (headerName == null) {
header = httpResponse.getAllHeaders();
} else {
header = httpResponse.getHeaders(headerName);
}
httpResponse.close();
return JSON.toJSONString(header);
} catch (IOException e) {
e.printStackTrace();
return "false";
}
}
/**
* 关闭客户端
*/
public void close() {
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
System.out.println("===>关闭http客户端失败!");
}
}
/**
* 设置默认url(用于指定调用环境)
* @param defaultUrl 带协议头的环境地址
*/
public void setDefaultUrl(String defaultUrl) {
this.defaultUrl = defaultUrl;
}
/**
* 设置默认请求头
* @param headerName 请求头的名字
* @param headerValue 请求头的值
*/
public void setDefaultHeader(String headerName, String headerValue) {
DEFAULT_HEADER.removeIf(header -> header.getName().equals(headerName));
DEFAULT_HEADER.add(new BasicHeader(headerName, headerValue));
}
/**
* 添加默认cookie,原默认也保留
* @param cookieValue cookie值,如 "token=****; accountId=****"
*/
public void addDefaultCookie(String cookieValue) {
String cookies = null;
for (Header header: DEFAULT_HEADER) {
if (header.getName().equals("Cookie")) {
cookies = header.getValue();
}
}
cookies = cookies "; " cookieValue;
DEFAULT_HEADER.add(new BasicHeader("Cookie", cookies));
}
/**
* 设置默认请求头
* @param cookieValue cookie值,如 "token=****; accountId=****"
*/
public void setDefaultCookie(String cookieValue) {
DEFAULT_HEADER.removeIf(header -> header.getName().equals("Cookie"));
DEFAULT_HEADER.add(new BasicHeader("Cookie", cookieValue));
}
/**
* 查询默认cookie中对应的值
* @param cookieName cookie名
* @return cookie值
*/
public String queryDefaultCookie(String cookieName) {
// cookies格式为 name1=value1; name2=value2
String cookies = "";
for (Header header: DEFAULT_HEADER) {
if (header.getName().equals("Cookie")) {
cookies = header.getValue();
}
}
int startIndex = cookies.indexOf(cookieName) cookieName.length() 1;
int endIndex = cookies.indexOf(";", startIndex);
return cookies.substring(startIndex, endIndex);
}
/**
* 执行http get请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public String get(String url) {
HttpGet httpGet = this.transformGet(url, null);
return this.httpBody(httpGet);
}
/**
* 执行http get请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param header 请求头,需要json格式
* @return 返回json格式
*/
public String get(String url, String header) {
HttpGet httpGet = this.transformGet(url, header);
return this.httpBody(httpGet);
}
/**
* 执行http get请求,并返回所有header
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public String getForHeader(String url) {
HttpGet httpGet = this.transformGet(url, null);
return this.httpHeader(httpGet, null);
}
/**
* 执行http get请求,并返回指定header
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param headerName 指定获取的返回头
* @return 返回json格式
*/
public String getForHeader(String url, String headerName) {
HttpGet httpGet = this.transformGet(url, null);
return this.httpHeader(httpGet, headerName);
}
/**
* 执行http get请求,并将返回的cookie设置为默认
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public void getAndCookie(String url) {
this.setDefaultCookie(getForCookie(url, null));
}
/**
* 执行http get请求,并返回所有cookie
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public String getForCookie(String url) {
return this.getForCookie(url, null);
}
/**
* 执行http get请求,并返回指定cookie
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param cookieName 指定获取的cookie名
* @return 返回json格式
*/
public String getForCookie(String url, String cookieName) {
StringBuilder result = new StringBuilder();
HttpGet httpGet = transformGet(url, null);
CookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
try {
httpClient.execute(httpGet);
List<Cookie> cookieList = cookieStore.getCookies();
for (Cookie cookie: cookieList) {
if (cookieName == null || cookieName.equals(cookie.getName())) {
result.append("; ").append(cookie.getName()).append("=").append(cookie.getValue());
}
}
if (result.length() > 2) {
result.delete(0, 2);
}
httpClient.close();
cookieStore.clear();
} catch (IOException e) {
e.printStackTrace();
}
return result.toString();
}
/**
* 执行http post请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public String post(String url) {
HttpPost httpPost = this.transformPost(url, null, null);
return this.httpBody(httpPost);
}
/**
* 执行http post请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param body 接口传入的请求体,application/json格式
* @return 返回json格式
*/
public String post(String url, String body) {
HttpPost httpPost = this.transformPost(url, body, null);
return this.httpBody(httpPost);
}
/**
* 执行http post请求
*
* @param url 完整的url地址-http://ip:port/path
* @param body 接口对应的POJO对象或Map对象,传入body中,application/json格式
* @param header 请求头,json字符串
* @return 返回json格式
*/
public String post(String url, String body, String header) {
HttpPost httpPost = this.transformPost(url, body, header);
return this.httpBody(httpPost);
}
/**
* 执行http post请求,返回header
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param body 接口传入的请求体,application/json格式
* @return 返回json格式
*/
public String postForHeader(String url, String body) {
HttpPost httpPost = this.transformPost(url, body, null);
return this.httpHeader(httpPost, null);
}
/**
* 执行http post请求,返回指定header
*
* @param url 完整的url地址-http://ip:port/path
* @param body 接口对应的POJO对象或Map对象,传入body中,application/json格式
* @param headerName 要获取的header名
* @return 返回json格式
*/
public String postForHeader(String url, String body, String headerName) {
HttpPost httpPost = this.transformPost(url, body, null);
return this.httpHeader(httpPost, headerName);
}
/**
* 执行http delete请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @return 返回json格式
*/
public String delete(String url) {
HttpDelete httpDelete = this.transformDelete(url, null);
return this.httpBody(httpDelete);
}
/**
* 执行http delete请求
*
* @param url 完整的url地址,可带参数-http://ip:port/path?param1=value1
* @param header 请求头,需要json格式
* @return 返回json格式
*/
public String delete(String url, String header) {
HttpDelete httpDelete = this.transformDelete(url, header);
return this.httpBody(httpDelete);
}
/**
* 执行http put请求
*
* @param url 完整的url地址-http://ip:port/path
* @return 返回json格式
*/
public String put(String url) {
HttpPut httpPut = this.transformPut(url, null, null);
return this.httpBody(httpPut);
}
/**
* 执行http put请求
*
* @param url 完整的url地址-http://ip:port/path
* @param body 接口对应的POJO对象或Map对象,传入body中,application/json格式
* @return 返回json格式
*/
public String put(String url, String body) {
HttpPut httpPut = this.transformPut(url, body, null);
return this.httpBody(httpPut);
}
/**
* 执行http put请求
*
* @param url 完整的url地址-http://ip:port/path
* @param body 接口对应的POJO对象或Map对象,传入body中,application/json格式
* @param header 请求头,json字符串
* @return 返回json格式
*/
public String put(String url, String body, String header) {
HttpPut httpPut = this.transformPut(url, body, header);
return this.httpBody(httpPut);
}
}
二、实现RPC接口调用
简介
通过ReferenceConfig类实现Rpc接口调用,可用于自动化,也可用于接口测试
依赖包
代码语言:java复制<!--dubbo 相关包-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.18</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
<!--dubbo 相关包-->
代码实现
代码语言:java复制public class RpcCommonClient {
/**
* 泛化调用rpc接口
*
* @param fullLocation 完整调用地址,如 "dubbo://ip:port/interface#method"
* @param paramType 被测rpc接口的入参类型
* @param paramValue 被测rpc接口的入参
* @return jason格式调用结果
*/
public String invoke(String fullLocation, String paramType, String paramValue) {
// 截取服务器地址
String url = fullLocation.substring(0, fullLocation.lastIndexOf("/"));
// 截取接口className
String interfaceClass = fullLocation.substring(fullLocation.lastIndexOf("/") 1, fullLocation.lastIndexOf("#"));
// 截取方法名
String methodName = fullLocation.substring(fullLocation.lastIndexOf("#") 1);
//创建ApplicationConfig
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("autoTester");
//创建服务引用配置
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
//设置接口,com.luoys.upgrade.uc.share.service.UserService
referenceConfig.setInterface(interfaceClass);
referenceConfig.setApplication(applicationConfig);
referenceConfig.setUrl(url);
//重点:设置为泛化调用
//注:不再推荐使用参数为布尔值的setGeneric函数
//应该使用referenceConfig.setGeneric("true")代替
referenceConfig.setGeneric("true");
//设置超时时间
referenceConfig.setTimeout(10000);
//获取服务,由于是泛化调用,所以获取的一定是GenericService类型
GenericService genericService = referenceConfig.get();
//使用GenericService类对象的$invoke方法可以代替原方法使用
//第一个参数是需要调用的方法名,queryByUserId
//第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
//第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
Object result;
if (paramType.equals(String.class.getName())) {
result = genericService.$invoke(methodName, new String[]{paramType}, new Object[]{paramValue});
} else if (paramType.equals(Integer.class.getName())) {
result = genericService.$invoke(methodName, new String[]{paramType}, new Object[]{Integer.valueOf(paramValue)});
} else if (paramType.equals(Long.class.getName())) {
result = genericService.$invoke(methodName, new String[]{paramType}, new Object[]{Long.valueOf(paramValue)});
} else {
result = genericService.$invoke(methodName, new String[]{paramType}, new Object[]{JSON.parseObject(paramValue)});
}
referenceConfig.destroy();
return JSON.toJSONString(result);
}
三、实现UI页面操作
简介
通过selenium实现网页的ui操作,可用于自动化
依赖包
代码语言:java复制<!--selenium自动化-->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.5.0</version>
</dependency>
<!--selenium自动化-->
<!--appium自动化-->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.1.1</version>
</dependency>
<!--appium自动化-->
代码实现
代码语言:java复制public class UiCommonClient {
// 显式等待最大时间(有问题,偶尔会失效)
private final Duration explicitDuration = Duration.ofSeconds(30L);
// 隐式等待最大时间
private final Duration implicitlyDuration = Duration.ofSeconds(10L);
// 流畅等待最大时间
private final Duration fluentDuration = Duration.ofSeconds(30L);
private WebDriverWait webDriverWait = null;
private Wait<WebDriver> wait = null;
private WebDriver driver = null;
private Actions actions = null;
/**
* 进程睡眠,强制等待
*
* @param millis 等待的时间-单位豪秒
*/
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取快捷键枚举值,不是快捷键则直接返回key本身
* @param key 字符
* @return 枚举值
*/
protected CharSequence getKey(String key) {
// 快捷键
if (key.equalsIgnoreCase("<ENTER>")) {
return Keys.ENTER;
} else if (key.equalsIgnoreCase("<TAB>")) {
return Keys.TAB;
}
// 普通输入
return key;
}
protected void settings() {
// 初始化actions
this.actions = new Actions(this.driver);
// 隐式等待设置
this.driver.manage().timeouts().implicitlyWait(this.implicitlyDuration);
// 显示等待初始化
this.webDriverWait = new WebDriverWait(this.driver, this.explicitDuration);
// 流畅等待初始化
this.wait = new FluentWait<WebDriver>(this.driver)
.withTimeout(this.fluentDuration)
.pollingEvery(Duration.ofSeconds(1))
.ignoring(NoSuchElementException.class);
}
/**
* 返回driver实例
*/
public WebDriver getDriver() {
return this.driver;
}
/**
* 用ChromeDriver初始化webdriver,参数默认,web模式;
* 如果已初始化过,则跳过
*/
public void initChromeWeb() {
// 不为null则表示已初始化
if (this.driver != null) {
return;
}
// 设置启动参数
ChromeOptions chromeOptions = new ChromeOptions();
// 解决DevToolsActivePort文件不存在的报错
chromeOptions.addArguments("--no-sandbox");
// 设置启动浏览器空白页
chromeOptions.addArguments("url=data:,");
// 最大化
chromeOptions.addArguments("--start-maximized");
// 谷歌禁用GPU加速
chromeOptions.addArguments("--disable-gpu");
// 隐藏滚动条
chromeOptions.addArguments("--hide-scrollbars");
// 后台运行
chromeOptions.addArguments("--headless");
// 去掉Chrome提示受到自动软件控制
chromeOptions.addArguments("disable-infobars");
// chromeOptions.addArguments("log-level=3");
chromeOptions.addArguments("--disable-dev-shm-usage");
chromeOptions.setLogLevel(ChromeDriverLogLevel.OFF);
this.driver = new ChromeDriver(chromeOptions);
this.settings();
// ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
}
/**
* 用ChromeDriver初始化webdriver,自定义参数
* 如果已初始化过,则跳过
*/
public void initChrome(String... options) {
if (this.driver != null) {
return;
}
Map<String, String> mobileEmulationMap = new HashMap<>();
List<String> cOptions = new ArrayList<>();
for (String option : options) {
// 带“,”的是H5配置项,键值对
if (option.matches(". ,. ")) {
String[] var = option.split(",");
mobileEmulationMap.put(var[0], var[1]);
} else {
cOptions.add(option);
}
}
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments(cOptions);
if (mobileEmulationMap.size() > 0) {
chromeOptions.setExperimentalOption("mobileEmulation", mobileEmulationMap);
}
this.driver = new ChromeDriver(chromeOptions);
this.settings();
}
/**
* 用ChromeDriver初始化webdriver,参数默认,H5模式;
* 如果已初始化过,则跳过
*/
public void initChromeH5() {
if (this.driver != null) {
return;
}
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--disable-dev-shm-usage");
chromeOptions.addArguments("--no-sandbox");
chromeOptions.addArguments("--disable-gpu");
chromeOptions.addArguments("--disable-gpu");
Map<String, String> mobileEmulationMap = new HashMap<>();
mobileEmulationMap.put("deviceName", "Samsung Galaxy S8 ");
chromeOptions.setExperimentalOption("mobileEmulation", mobileEmulationMap);
chromeOptions.addArguments("--headless");
this.driver = new ChromeDriver(chromeOptions);
this.settings();
}
/**
* 用AndroidDriver初始化webdriver,参数默认;
* 如果已初始化过,则跳过
*/
public void initAndroid() {
// 连模拟器命令 adb connect 127.0.0.1:7555
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability("platformName", "Android"); //指定测试平台
desiredCapabilities.setCapability("deviceName", "127.0.0.1:7555"); //指定测试机的ID,通过adb命令`adb devices`获取
// cap.setCapability("app", "C:\other\q391m11market01xtg101s.apk");
desiredCapabilities.setCapability("appPackage", "com.qmxsppa.novelreader");
desiredCapabilities.setCapability("appActivity", "com.marketplaceapp.novelmatthew.mvp.ui.activity.main.AndroidAppActivity");
//A new session could not be created的解决方法
// cap.setCapability("appWaitActivity", "com.qmxsppa.novelreader");
// //每次启动时覆盖session,否则第二次后运行会报错不能新建session
// cap.setCapability("sessionOverride", true);
try {
this.driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), desiredCapabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
this.settings();
}
/**
* 刷新页面
*
* @param url 被测网站主页或登录页
*/
public void openUrl(String url) {
this.driver.get(url);
}
/**
* 关闭浏览器且关闭资源
*/
public void quit() {
if (this.driver == null) {
return;
}
this.driver.quit();
this.driver = null;
}
/**
* 获取同类别的自动化元素列表
*
* @param xpath 元素的xpath
* @return 所有符合条件的元素
*/
protected List<WebElement> getElements(String xpath) {
return this.driver.findElements(By.xpath(xpath));
}
/**
* 鼠标点击指定元素
*
* @param xpath 元素的xpath
*/
public void click(String xpath) {
this.click(xpath, "0");
}
/**
* 鼠标点击指定元素
*
* @param xpath 元素的xpath
* @param index list下标,从0开始
*/
public void click(String xpath, String index) {
// 这里不能用显式等待,有的控件等不到,却可以点击
WebElement webElement = this.wait.until(driver -> driver.findElements(By.xpath(xpath)).get(Integer.parseInt(index)));
this.actions.click(webElement).perform();
}
/**
* 通过js的形式点击页面元素
* @param xpath 页面元素的xpath
*/
public void clickByJs(String xpath) {
this.executeJs("arguments[0].click();", xpath);
}
/**
* 先鼠标移动到指定元素上,然后鼠标点击
*
* @param xpath 元素的xpath
*/
public void clickByMove(String xpath) {
WebElement webElement = this.wait.until(driver -> driver.findElement(By.xpath(xpath)));
this.actions.moveToElement(webElement).click().perform();
}
/**
* 先清除输入框的内容,再往指定元素中输入字符
*
* @param key 输入的字符串或快捷键
*/
public void sendKey(String key) {
this.actions.sendKeys(this.getKey(key)).perform();
}
/**
* 先清除输入框的内容,再往指定元素中输入字符
*
* @param xpath 元素的xpath
* @param key 输入的字符串或按键
*/
public void sendKey(String xpath, String key) {
WebElement webElement = this.wait.until(driver -> driver.findElement(By.xpath(xpath)));
// 再强制等0.5s会更稳定(显示、隐式、流畅等待都有小概率操作失败)
this.sleep(500L);
webElement.clear();
this.actions.sendKeys(webElement, this.getKey(key)).perform();
}
/**
* 先清除输入框的内容,再往指定元素中输入字符
*
* @param xpath 元素的xpath
* @param key 输入的字符串
* @param shortcutKey 快捷键
*/
public void sendKey(String xpath, String key, String shortcutKey) {
WebElement webElement = this.wait.until(driver -> driver.findElement(By.xpath(xpath)));
// 再强制等0.5s会更稳定(显示、隐式、流畅等待都有小概率操作失败)
this.sleep(500L);
webElement.clear();
this.actions.sendKeys(webElement, this.getKey(key), this.getKey(shortcutKey)).perform();
}
/**
* 先清除输入框的内容,再往指定元素中输入字符,再按Enter键
*
* @param xpath 元素的xpath
* @param key 输入的字符串
*/
public void sendKeyByEnter(String xpath, String key) {
WebElement webElement = this.wait.until(driver -> driver.findElement(By.xpath(xpath)));
// 再强制等0.5s会更稳定(显示、隐式、流畅等待都有小概率操作失败)
this.sleep(500L);
webElement.clear();
this.actions.sendKeys(webElement, key, Keys.ENTER).perform();
}
/**
* 鼠标移动到指定元素上
*
* @param xpath 元素的xpath
*/
public void move(String xpath) {
WebElement webElement = this.wait.until(driver -> driver.findElement(By.xpath(xpath)));
this.actions.moveToElement(webElement).perform();
}
/**
* 从一个元素位置拖拽到另一个元素位置
* @param fromXpath
* @param toXpath
*/
public void drag(String fromXpath, String toXpath) {
WebElement from = this.wait.until(driver -> driver.findElement(By.xpath(fromXpath)));
WebElement to = this.wait.until(driver -> driver.findElement(By.xpath(toXpath)));
this.actions.dragAndDrop(from, to).perform();
}
/**
* 执行java script脚本
*
* @param javaScript 脚本
*/
public void executeJs(String javaScript) {
((JavascriptExecutor) this.driver).executeScript(javaScript);
}
/**
* 执行java script脚本
*
* @param javaScript 脚本
* @param xpath 元素xpath
*/
public void executeJs(String javaScript, String xpath) {
WebElement webElement = this.driver.findElement(By.xpath(xpath));
((JavascriptExecutor) this.driver).executeScript(javaScript, webElement);
}
/**
* 切换到最后一个标签页,并关闭其它;
* 如果只有一个标签页,则不处理
*/
public void switchTab() {
Set<String> windows = this.driver.getWindowHandles();
if (windows.size() <= 1) {
return;
}
// 先关闭前面所有标签页
for (int i = windows.size() - 2; i >= 0; i--) {
this.driver.switchTo().window(windows.toArray()[i].toString());
this.driver.close();
}
// 再切换至最后标签页
this.driver.switchTo().window(windows.toArray()[windows.size() - 1].toString());
}
/**
* 删除所有cookies
*/
public void clearCookies() {
this.driver.manage().deleteAllCookies();
}
}
四、实现SQL执行
简介
通过JdbcTemplate实现Sql执行,可用于自动化,也可用于准备测试数据
依赖包
代码语言:java复制<!--jdbc template相关的包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--jdbc template相关的包-->
代码实现
代码语言:java复制public class SqlCommonClient {
private JdbcTemplate jdbcTemplate = null;
private DriverManagerDataSource dataSource = null;
/**
* 执行sql
* 支持增删改查,删改查一定要带条件
*
* @param sql 要执行的sql
* @return -
*/
public String execute(String sql) {
String result;
// 根据sql语句类型,选择不同的方法执行
if (sql.toUpperCase().matches("^INSERT INTO . ")) {
result = this.insert(sql);
} else if (sql.toUpperCase().matches("^DELETE FROM [A-Z0-9_] WHERE . ")) {
result = this.delete(sql);
} else if (sql.toUpperCase().matches("^UPDATE [A-Z0-9_] SET . WHERE . ")) {
result = this.update(sql);
} else if (sql.toUpperCase().matches("^SELECT . FROM [A-Z0-9_] (WHERE . )?")) {
// 查询单列
if (sql.toUpperCase().matches("^SELECT [^,*] FROM [A-Z0-9_] WHERE . ")) {
result = this.selectOneCell(sql);
} else {
result = this.select(sql);
}
} else {
result = "不支持sql类型 ";
}
return result;
}
/**
* 初始化数据源
* 如果已初始化,则跳过
*
*/
public void init(String driver, String url, String userName, String password) {
if (this.jdbcTemplate != null) {
return;
}
this.jdbcTemplate = new JdbcTemplate();
this.dataSource = new DriverManagerDataSource();
this.dataSource.setDriverClassName(driver);
this.dataSource.setUrl(url);
this.dataSource.setUsername(userName);
this.dataSource.setPassword(password);
this.jdbcTemplate.setDataSource(this.dataSource);
}
/**
* 把更新sql转换为查询总行数的sql,查询sql以更新sql的条件为条件
*
* @param updateSql 更新sql,注意字段间空格只能有一个
* @return select count(1) from 更新的表名 更新的条件
*/
private String transformUpdate2Select(String updateSql) {
int endIndex = updateSql.toLowerCase().indexOf(" set ");
String tableName = updateSql.substring(7, endIndex);
int startIndex = updateSql.toLowerCase().indexOf(" where ");
String condition = updateSql.substring(startIndex);
return "select count(1) from " tableName condition;
}
/**
* 把删除sql转换为查询总行数的sql,查询sql以更新sql的条件为条件
*
* @param deleteSql 删除sql,注意字段间空格只能有一个
* @return select count(1) from 删除的表名 删除的条件
*/
private String transformDelete2Select(String deleteSql) {
int startIndex = deleteSql.toLowerCase().indexOf(" from ");
String condition = deleteSql.substring(startIndex);
return "select count(1) " condition;
}
/**
* 执行更新sql语句
*
* @param sql 要执行的sql
* @return 执行结果
*/
private String update(String sql) {
int effectRow = this.count(transformUpdate2Select(sql));
if (effectRow > 200) {
return "一次更新超过200行,请确认sql条件是否正确";
} else if (effectRow == 0) {
return "查无此类数据,不需要更新";
}
return String.valueOf(this.jdbcTemplate.update(sql));
}
/**
* 执行插入sql语句
*
* @param sql 要执行的sql
* @return 执行结果
*/
private String insert(String sql) {
return String.valueOf(this.jdbcTemplate.update(sql));
}
/**
* 执行查询sql语句
*
* @param sql 要执行的sql
* @return 执行结果
*/
private String select(String sql) {
return this.jdbcTemplate.queryForList(sql).toString();
}
/**
* 执行删除sql语句
*
* @param sql 要执行的sql
* @return 执行结果
*/
private String delete(String sql) {
int effectRow = this.count(transformDelete2Select(sql));
if (effectRow > 50) {
return "一次删除超过50行,请确认sql条件是否正确";
} else if (effectRow == 0) {
return "查无此类数据,不需要删除";
}
return String.valueOf(this.jdbcTemplate.update(sql));
}
/**
* 查询总条数
*
* @param sql 查询语句
* @return 总条数
*/
private Integer count(String sql) {
Map<String, Object> result = this.jdbcTemplate.queryForMap(sql);
String COUNT = "count(1)";
return Integer.valueOf(result.get(COUNT).toString());
}
/**
* 查询单列
* 时间会转为yyyy-MM-dd HH:mm:ss格式
*
* @param sql 查询sql
* @return 只返回第一行的值,不带列名
*/
private String selectOneCell(String sql) {
String[] sqlList = sql.split("[ ] ");
Map<String, Object> result = this.jdbcTemplate.queryForList(sql).get(0);
// 根据列名取值
Object value = result.get(sqlList[1]);
if (value == null) {
return "null";
}
//时间格式要转换
if (value.getClass().getName().equals("java.time.LocalDateTime")) {
LocalDateTime time = (LocalDateTime) value;
DateTimeFormatter dateTimeFormatter= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.of("Asia/Shanghai"));
return dateTimeFormatter.format(time);
} else {
return value.toString();
}
}
}
五、部分工具方法
简介
取Json字符串指定key对应的值、获取excel的值
依赖包
代码语言:java复制<!-- 引入fastjson https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.16</version>
</dependency>
代码语言:java复制<!--excel 相关包-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!--excel 相关包-->
代码实现
代码语言:java复制/**
* 通过递归的方式,计算某关键字在字符串中出现的次数
*
* @param var 字符串
* @param key 关键字
* @param count 出现次数,初始填0
* @return 出现的总次数
*/
private int countByString(String var, String key, int count) {
if (!var.contains(key)) {
return count;
} else {
return this.countByString(var.substring(var.indexOf(key) key.length()), key, count 1);
}
}
/**
* 查询某节点的数据在JSON字符串中的结束位置
* (递归版本)
*
* @param json 完整json字符串
* @param typeLeft 数据类型,{或[
* @param typeRight 数据类型,}或]
* @param startIndex 真实的节点数据起始位置,位置需在":"之后
* @param endIndex 默认传0
* @return 节点数据的终止位置
*/
private int getNodeEnd(String json, String typeLeft, String typeRight, int startIndex, int endIndex) {
//获取最近一个}或]的位置
int nextEndIndex = json.indexOf(typeRight, endIndex) 1;
if (nextEndIndex >= json.length()) {
log.warn("节点值不规范");
return -1;
} else if (this.countByString(json.substring(startIndex, nextEndIndex), typeLeft, 0)
== this.countByString(json.substring(startIndex, nextEndIndex), typeRight, 0)) {
return nextEndIndex;
} else {
return this.getNodeEnd(json, typeLeft, typeRight, startIndex, nextEndIndex);
}
}
/**
* 查询某节点的数据在JSON字符串中的结束位置
*
* @param json 完整json字符串
* @param startIndex 真实的节点数据起始位置,位置需在":"之后
* @return 节点数据的终止位置
*/
private int getNodeEnd(String json, int startIndex) {
String jsonData = json.substring(startIndex);
String typeLeft;
String typeRight;
if (jsonData.charAt(0) == '{') {
typeLeft = "{";
typeRight = "}";
} else if (jsonData.charAt(0) == '[') {
typeLeft = "[";
typeRight = "]";
} else if (!jsonData.contains(",")) {
//刚好是最后一个节点,且该节点的值不是数组或对象
return startIndex jsonData.replace("}", "").replace("]", "").length();
} else {
//非最后一个节点,且节点值不是数组或对象
return startIndex jsonData.indexOf(",");
}
//节点的值是数组或对象,
return this.getNodeEnd(json, typeLeft, typeRight, startIndex, 0);
}
/**
* 通过key值,获取对应得json值
*
* @param key json字符串的key
* @param json json字符串本身
* @return key对应的value
*/
public String getJson(String key, String json) {
JSONObject jsonObject = JSON.parseObject(json);
return jsonObject.getString(key);
}
/**
* 根据JSON字符串中的节点名,取出对应的值;如果同名节点有多个,则只取第 index 个
* 如JSON中包含数组或对象,也纳入取值
*
* @param key json字符串中的节点名
* @param json 完整的json字符串
* @return 名称对应的值
*/
public String getJsonAny(String key, String json) {
String jsonName = """ key "":";
int startIndex = json.indexOf(jsonName) jsonName.length();
int endIndex = getNodeEnd(json, startIndex);
String jsonResult = json.substring(startIndex, endIndex);
//如果是字符串,去除首尾双引号
if (jsonResult.startsWith(""")) {
jsonResult = jsonResult.substring(1, jsonResult.length() - 1);
}
log.info("n====>获取到的json节点值为:{}", jsonResult);
return jsonResult;
}
代码语言:java复制/**
* 获取 从指定开始行和开始列起,到最后一行最后一列 的数据
* 默认第一个工作表
*
* @param filePath 带文件名和文件后缀的完整路径
* @param startLine 开始行,0开始
* @param startColumn 开始列,0开始
* @return 二维列表数据
*/
public static String[][] getExcelContent(String filePath, String sheetName, int startLine, int startColumn) {
InputStream inputStream;
HSSFWorkbook workbook;
HSSFSheet sheet;
HSSFRow row;
HSSFCell cell;
try {
// 初始化
inputStream = new FileInputStream(filePath);
workbook = new HSSFWorkbook(inputStream);
sheet = sheetName == null ? workbook.getSheetAt(0) : workbook.getSheet(sheetName);
// 获取行数和第一行的列数
int endLine = sheet.getLastRowNum() 1;
int endColumn = sheet.getRow(0).getPhysicalNumberOfCells();
String[][] allData = new String[endLine - startLine][endColumn - startColumn];
// 获取工作表的数据,不一定是第0行
for (int i = startLine; i < endLine; i ) {
row = sheet.getRow(i);
for (int j = startColumn; j < endColumn; j ) {
cell = row == null ? null : row.getCell(j);
// 数组要从第0行第0列开始
allData[i - startLine][j - startColumn] = cell == null ? "" : cell.toString();
}
}
// 关闭资源
inputStream.close();
return allData;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}