背景

在自动化实施中我们可能会选择一定的方案来针对业务。现在来看业务服务形式主要是web和wap,所以选择了selenium开源自动化测试工具来满足我们的自动化需求。但是selenium自身设计并不能满足我们所有的应用场景,如:get方法添加header。selenium原生设计就不支持这个。

所以问题是:selenium如何支持header操作,来满足业务自动化场景?

解决思路

Webdriver不包含用于执行此操作的API。有关详细信息,请参阅Selenium跟踪器的问题141。该问题的标题说它是关于响应标头但是决定Selenium在此问题的范围内不包含请求标头的API。

有关添加API以设置请求标头的几个问题已标记为重复:第一第二第三

以下几种可能性:

  1. 使用另一个驱动程序/库而不是selenium(这个还要看编程所在领域给的库或包:python selenium-wire
  2. 编写特定于浏览器的插件(或查找现有插件),允许您为请求添加标头。(现在好像是有插件:chrome和modify-headers firefox,但是对浏览器版本有要求)
  3. 使用browsermob-proxy或其他一些代理。(代理是个好主意)
  4. Selenium没有内置方法来执行此操作,但可以通过使用fiddler等代理来完成。Fiddler还提供了FiddlerCore组件的仅API版本,以及对所有代理设置和数据的编程访问,从而允许您修改http响应的标头。(同第三代理很像,基本是一样的)

在大多数情况下,会选择3:为什么? 这个取决于你的selenium的设计(二次开发)

请注意,Ghostdriver有一个API,但其他驱动程序不支持它。

关于browserMob

通常情况下,直接使用Selenium就足够了,但是Selenium有很多自身的问题,比如不支持修改request的参数,比如很重要的headers。headers其实是一些模拟测试时用来模拟不同的访问和测试安全的重要元信息,从一些github issues看Selenium开发方似乎拒绝考虑加入headers修改功能,以自动化测试组件不应该让用户修改headers的理由应付用户,并让大家使用BrowserMobProxy去模拟。如果webdriver也是可控的,这完全是可以做到的,可能会需要协调不同浏览器的webdriver开发者会有点麻烦;所以也不知道是开发方懒,还是和browsermobproxy的开发方有什么关系。。而弹出浏览器并能提供足够的控制功能的框架目前暂没见到其他能与selenium媲美的。(PhantomJS是在服务端提供了个解析,但并不会事实弹出浏览器模仿真正的浏览器行为,也就是说一些前端涉及鼠标、悬停等事件的脚本并不能支持)

browserMob-proxy 官网
browserMob-proxy GitHub

这个browserMob-prox支持两种用法:

一是嵌入式:把代理服务写到case里或者自定义的selenium服务里,这里可能会有一个问题,就是服务容器的冲突(browserMob用的好像是jetty,如果你的selenium服务是tomcat的就会有报错);

二是使用远程方式,browserMob-proxy服务独立部署,脚本动态绑定代理并使用;

在灵活性上、独立性上,我们会选择第二种方式:远程访问

实践: browserMob远程代理

即browsermob在外部服务独立运行,本地脚本或者selenium服务只通过restful与其通信,期间包括先注册端口和设置headers等,seleniumProxy也得配置到对应的socket去。

部署browsermob也很简单,官网 下载部署版本或从github下载项目tag版本命令行执行 mvn clean package -U打包,然后找到对应的jar文件进行源码安装。源码安装的话在browsermob-dist/target/ 下可以找到bin结尾的目录,里面就是可执行文件(*nix和windows bat都有),copy到你需要的目录即可。

我们选择最直接的部署方式:下载已打包好的,直接进行部署
在这里插入图片描述

bin目录下有bat和shell文件,直接执行就行

readme.md文件里有教程:如:browsermob-proxy -port 8080 启动当前服务

[INFO  2019-06-13T15:28:18,818 net.lightbody.bmp.proxy.Main] (main) Starting BrowserMob Proxy version 2.1.4 
[INFO  2019-06-13T15:28:18,851 org.eclipse.jetty.util.log] (main) jetty-7.x.y-SNAPSHOT 
[INFO  2019-06-13T15:28:18,938 org.eclipse.jetty.util.log] (main) started o.e.j.s.ServletContextHandler{/,null} 
[INFO  2019-06-13T15:28:19,075 org.eclipse.jetty.util.log] (main) Started SelectChannelConnector@0.0.0.0:8080 
[INFO  2019-06-13T15:28:26,987 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Starting proxy at address: /****:11011 
[INFO  2019-06-13T15:28:27,010 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Proxy listening with TCP transport 
[INFO  2019-06-13T15:28:27,125 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Proxy started at address: /****:11011 
[INFO  2019-06-13T15:28:28,151 org.littleshoot.proxy.impl.ProxyToServerConnection] (LittleProxy-0-ProxyToServerWorker-0) (HANDSHAKING) [id: 0x2522675f, 

怎么在用例里使用呢?(详阅官网文档,这里不赘诉了)

注意:这里要注意两个端口号:browserMob作为一个服务,为你提供服务的端口是8080或者其他你自定义的(来自-port);另一个是你用例脚本使用的端口(如:11011),你会把你的header及其他规则注册到这个端口11011上(这个代理端口是可以动态注册的),然后脚本上绑定的代理就是这个*.*.*.*:11011

maven: 解决下依赖问题

<dependency>
      <groupId>net.lightbody.bmp</groupId>
      <artifactId>browsermob-core</artifactId>
      <version>${browsermob.version}</version>
    </dependency>
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
    </dependency>

java:实例代码(记得引入包)

DesiredCapabilities capabilities = DesiredCapabilities.chrome();
Map<String, Object> chromeOptions = new HashMap<>();

if(isheadless!=null && isheadless == 1){  //是否走无头浏览器
    List<String> chromeargslist = new ArrayList<>();
    chromeargslist.add("--headless");
    chromeOptions.put("args",chromeargslist);
}

// jwensh 处理代理的问题 20190613
if(useProxy == 1){
    if(proxyHeaders.trim() != "" && proxyHeaders.trim() != "{}"){
        // 向代理服务注册工作代理端口
        Map<String, Object> registerPort = new HashMap<>();
        registerPort.put("bindAddress", proxyServerIP);
        registerPort.put("port", "11011");
        HttpUtil.sendHttpPost(String.format("http://%s:%s/proxy", proxyServerIP, "8080"), registerPort);
        try {
		   //这个就是讲header转化为header注册到代理服务中去
           JSONObject ph = JSONObject.parseObject(proxyHeaders);
		   //post请求把我的代理配置注册到代理里
           HttpUtil.sendJson(String.format("http://%s:%s/proxy/%s/headers", proxyServerIP, "8080", "11011"), ph);
           // 指定工作的代理而不是代理服务
           String PROXY = proxyServerIP + ":" + "11011";
           Proxy seleniumProxy = ClientUtil.createSeleniumProxy(new InetSocketAddress(proxyServerIP, 11011));
           seleniumProxy.setHttpProxy(PROXY).setSslProxy(PROXY);
           // 注册代理到浏览器属性上
           capabilities.setCapability(CapabilityType.PROXY, seleniumProxy);
           logger.info("代理设置完毕!");
         }catch (Exception e){
            logger.error("config selenium proxy happend error");
         }
     }
}

if(isheadless!=null && isheadless==1){
     capabilities.setCapability(ChromeOptions.CAPABILITY,chromeOptions);
}

return new RemoteWebDriver(weburl,capabilities);

如果你是python selenium也不用担心,同样适用 (pip install browsermob-proxy)

总结下

  1. 使用代理的方式,就像我们平时测试app一样适用fiddler或Charles来拦截并修改内容一样,并不会影响我们的服务(browserMob支持https)
  2. 对于我当前的selenium平台服务来说,会一直使用同一个代理端口处理多个case且并行的操作,可能会有header内容丢失的可能,但是目前不会,量级还没有上,如果出现这个问题,可以将代理端口配置成多个
  3. 代理解决了selenium不支持header的问题;简单、好用。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐