Selenium [Web 自动化] Web 自动化的同时进行接口校验

bauul · 2019年02月27日 · 最后由 bauul 回复于 2019年03月05日 · 3030 次阅读

缘由

当前做的项目中很多页面存在一定的共性,所以思考了一下 Web 自动化的可行性,
目前基本上做了一小块信息抽取的功能,目的是

  1. 给到产品验收
  2. 做版本对比
  3. 后续可以做 PRD 对比,即拿文档中的要求直接与开发出来的结果进行对比

在做 UI 自动化的过程中,脑子里迸出来一个想法,是不是可以同时对接口数据进行校验?
网上一搜,丫的还真有,还在 6 年多前就有了解决方案
于是有了本文

技术基础

  1. selenium
  2. xpath
  3. testNG
  4. browsermob-proxy
  5. java
  6. maven

Pom

<properties>
    <aspectj.version>1.8.10</aspectj.version>
    <allure.version>1.5.4</allure.version>
    <testng.version>6.11</testng.version>
    <okhttp3.version>3.10.0</okhttp3.version>
    <lombok.version>1.16.18</lombok.version>
    <slf4j.version>1.7.25</slf4j.version>
    <logback.version>1.2.3</logback.version>
    <assertj.version>3.8.0</assertj.version>
    <fastjson.version>1.2.47</fastjson.version>
    <mysql.connector.version>8.0.11</mysql.connector.version>
    <jedis.version>2.9.0</jedis.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>

    <dependency>
        <groupId>net.lightbody.bmp</groupId>
        <artifactId>browsermob-core</artifactId>
        <version>2.1.5</version>
    </dependency>

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>3.141.59</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
    </dependency>

    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>3.0.6</version>
    </dependency>

    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>${assertj.version}</version>
    </dependency>

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>${jedis.version}</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.connector.version}</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>${logback.version}</version>
    </dependency>

    <dependency>
        <groupId>org.codehaus.janino</groupId>
        <artifactId>janino</artifactId>
        <version>3.0.7</version>
    </dependency>

    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>${testng.version}</version>
    </dependency>

    <dependency>
        <groupId>ru.yandex.qatools.allure</groupId>
        <artifactId>allure-testng-adaptor</artifactId>
        <version>${allure.version}</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>${okhttp3.version}</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>logging-interceptor</artifactId>
        <version>${okhttp3.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.0.1</version>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>23.0</version>
    </dependency>

</dependencies>

主要思路

import com.wz.pages.Login;
import net.lightbody.bmp.BrowserMobProxy;
import net.lightbody.bmp.BrowserMobProxyServer;
import net.lightbody.bmp.client.ClientUtil;
import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.proxy.CaptureType;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;

public class Demo {

    @Test
    public void t1() throws IOException {
//        System.setProperty ("jsse.enableSNIExtension", "false");
        BrowserMobProxy browserMobProxy = new BrowserMobProxyServer();
        browserMobProxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT);
        browserMobProxy.start();

        Proxy seleniumProxy = ClientUtil.createSeleniumProxy(browserMobProxy);

        System.setProperty("webdriver.chrome.driver", Demo.class.getResource("/chromedriver").getPath());
        ChromeOptions chromeOptions = new ChromeOptions();

        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.PROXY, seleniumProxy);
        capabilities.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, false);
        chromeOptions.merge(capabilities);

        WebDriver driver = new ChromeDriver(chromeOptions);

        browserMobProxy.newHar("https://wzzzzzzzzzzzz.com");
        Login.loginAccount(driver, "https://wzzzzzzzzzzzz","01999888777", "Clark@01999888777");

        Har har = browserMobProxy.getHar();
        har.writeTo(new File("demo.har"));

        driver.close();

    }
}

还是直接上代码吧,可以看出来,

  1. 配置代理
  2. 设置需要抓取请求体,响应体 ==> 这里可以不设置,那么抓出来 har 包里面就没有请求体,响应体信息
  3. 配置 chrome 参数,启动 chrome
  4. 进行 web 测试
  5. web 测试结束后,保存抓包数据

结果

{
  "pageref": "https://wzzzzzzzzzzzz.com",
  "startedDateTime": "2019-02-27T13:25:31.188Z",
  "request": {
    "method": "POST",
    "url": "https://wzzzzzzzzzzzz.com/tenant/v1/api/user/account/login",
    "httpVersion": "HTTP/1.1",
    "cookies": [

    ],
    "headers": [

    ],
    "queryString": [

    ],
    "postData": {
      "mimeType": "application/x-www-form-urlencoded",
      "params": [
        {
          "name": "account",
          "value": "01999888777",
          "comment": ""
        },
        {
          "name": "password",
          "value": "Clark@01999888777",
          "comment": ""
        }
      ],
      "comment": ""
    },
    "headersSize": 505,
    "bodySize": 48,
    "comment": ""
  },
  "response": {
    "status": 0,
    "statusText": "",
    "httpVersion": "unknown",
    "cookies": [

    ],
    "headers": [

    ],
    "content": {
      "size": 0,
      "mimeType": "",
      "comment": ""
    },
    "redirectURL": "",
    "headersSize": -1,
    "bodySize": -1,
    "comment": "",
    "_error": "No response received"
  },
  "cache": {

  },
  "timings": {
    "comment": "",
    "dns": 0,
    "connect": 84,
    "send": 0,
    "wait": 0,
    "receive": 0,
    "blocked": 30,
    "ssl": 45
  },
  "serverIPAddress": "139.198.176.127",
  "comment": "",
  "time": 115
}

有了 har 包,再对其进行解析,就可以做响应校验,更多的数据校验了

问题

  1. 有看到 BrowserMobProxy 可以设置过滤器,但是暂时没实践出来,也没有搜索到相关的例子, github 官网上的不像是过滤器,是拦截器,而我想要 XHR,或是按 url 地址进行正则过滤 ==> 暂时的解决方法:可以对 Har 的内容进行过滤
  2. 之前还有一个想法是通过 selenium 调用 javascript 方法或 chrome 的 api 来获取 XHR 请求数据,没试成功
  3. 看到别人写好的 Har 代码,再看看自己的,我日,咋没早点发现呢,花了自己好几天时间,直接引他的包就完了, 当然在这过程中也踩了坑学习到了如何对枚举类进行反序列化
  4. 当前的测试环境是 HTTPS 协议的,在日志中报 SSL 的错误,虽然不影响测试,但是就是感觉别扭 io.netty.handler.codec.DecoderException: javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name

参考

  1. https://github.com/lightbody/browsermob-proxy
  2. https://sites.google.com/a/chromium.org/chromedriver/system/app/pages/search?scope=search-site&q=Request
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 12 条回复 时间 点赞

我们这边 web 后台自动化也是要考虑这块,我希望最好还是实时的检验,这样更好的确保当前请求响应的数据跟页面展示的数据是否一致,确保 ui 这层没有调错数据

bauul #11 · 2019年02月28日 Author
CC 回复

嗯嗯,可以搞的,我也是基于 okhttp 封装的请求😁

我们 SDK 打点自动化方案,是专门做 request 和 response 校验的,只不过是移动端方案

simple 回复

我理解的话这个 web 和移动端应该差不多

之前用了 Browsermob ,不过后来放弃换 AnyProxy 了
Browsermob-proxy 验证埋点和下载功能

黑水 回复

嗯,看过你的帖子了,论坛中唯一的一篇,想要了解一下,为什么换 AnyProxy 的?
目前来看,拿到 Har 数据,是比较方便解析的,其中还有一些性能数据后续可以用起来

simple 回复

移动端有对应的代理方案吗?我这边相当需要

CC 回复

你看看这篇文章,是不是你想要的

bauul 回复

其实不是一定要 anyproxy,只不过刚好在社区里看到阿里开源的这个代理很好用,我们的抓包都是 https 加密的,所以需要在抓包过程中自动解密 request 参数和 response body,因此开源代理更灵活一些;拿 Har 数据我们也用到了,用了 mitmproxy 来抓的,做性能分析用

bauul 回复
  1. Browsermob,用自签名 HTTPS 证书的时候解析不了(没找到原因,也许我用的方法不对)
  2. AnyProxy 有 UI 界面,手工测试、调试的时候可以用同样的工具、代理规则、准备好的测试模板数据
  3. Browsermob 断点调试的时候,一个页面有 10 个请求,只想断其中一个请求,其余 9 个也会被断住,界面判断超时就几秒,这样享受不到断点的好处了。如果都要改代码重启,AnyProxy + 动态语言方便多了
  4. 接口都是 JSON 的,JavaScript 处理 JSON 相比 Java 方便超级多
  5. 接口都是 HTTP 的,JavaScript 处理 HTTP 相比 Java 方便超级多

代理还有这两个,不过写的挺舒服就没去尝试了
https://github.com/mitmproxy/mitmproxy/
https://github.com/snail007/goproxy

黑水 回复

感谢推荐👍
之前下过 anyproxy,感觉不是太友好,可能我还没摸熟😜
browsermob 就先踩个坑,暂时还没有那么多精力搞脚本,主要还在搞业务的功能测试

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册