HTTP、RPC、UI、SQL自动化封装示例(JAVA)

2024-08-16 15:24:46 浏览数 (4)

以下是基于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;
    }
}

0 人点赞