前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTTP、RPC、UI、SQL自动化封装示例(JAVA)

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

原创
作者头像
骆小生
修改2023-04-11 19:54:05
1.9K0
修改2023-04-11 19:54:05
举报

以下是基于java,分别通过HttpClient、Dubbo、Selenium、JdbcTemplate实现Http、RPC、UI、SQL操作的示例代码。导入依赖包后可直接复制代码执行。

代码最大的特点是公共方法入参和出参类型全是String。可直接用于自动化,也可用于准备数据。

一、实现HTTP接口调用

简介

使用HttpClient实现Http接口的调用,可用于接口自动化,也可用于接口测试。

依赖包

代码语言:javascript
复制
<!--http接口调用-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
<!--http接口调用-->

代码实现

代码语言:javascript
复制
//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接口调用,可用于自动化,也可用于接口测试

依赖包

代码语言:javascript
复制
<!--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 相关包-->

代码实现

代码语言:javascript
复制
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操作,可用于自动化

依赖包

代码语言:javascript
复制
<!--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自动化-->

代码实现

代码语言:javascript
复制
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执行,可用于自动化,也可用于准备测试数据

依赖包

代码语言:javascript
复制
<!--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相关的包-->

代码实现

代码语言:javascript
复制
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的值

依赖包

代码语言:javascript
复制
<!-- 引入fastjson  https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.16</version>
</dependency>
代码语言:javascript
复制
<!--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 相关包-->

代码实现

代码语言:javascript
复制
/**
 * 通过递归的方式,计算某关键字在字符串中出现的次数
 *
 * @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;
}

代码语言:javascript
复制
/**
 * 获取 从指定开始行和开始列起,到最后一行最后一列 的数据
 * 默认第一个工作表
 *
 * @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;
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、实现HTTP接口调用
  • 二、实现RPC接口调用
  • 三、实现UI页面操作
  • 四、实现SQL执行
  • 五、部分工具方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档