web自动化原理揭秘

发表于:2020-3-16 11:30

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:韬哥    来源:博客园

  做过两年自动化测试的小伙伴说web自动化测试真的不难,无非就是一些浏览器操作,页面元素操作,常规的情况很容易处理,再学一学特殊元素的处理,基本就能应付项目的测试了。
  这个话倒没错,但是真正要学好自动化测试,深入自动化,并不是那么简单。首先你得懂原理吧,原理不懂,你就不知道怎么解决一些异常情况,也无法完成拓展。其次你得学会写自己的测试框架吧,一个项目写了100个测试类,都是零散的脚本,没有任何设计而言,都是纯粹的业务代码,那我可以说,换了项目你这些脚本就成了垃圾,因此,我们要做自动化,要成为自动化大牛,就一定要花时间去要搞清楚自动化实现的原理,并且学会自己去实现自动化测试框架,乃至于自动化测试平台。
  下面一段代码实现了一个很简单的功能:
  1.打开浏览器
  2.访问页面“http://ke.qq.com”
  3.定位到页面的搜索框
  4.输入查询数据
  5.定位搜索按钮
  6.点击搜索按钮,完成搜索
  代码如下图: 
   package web.demo;
  import org.openqa.selenium.By;
  import org.openqa.selenium.WebDriver;
  import org.openqa.selenium.WebElement;
  import org.openqa.selenium.firefox.FirefoxDriver;
  import org.testng.annotations.Test;
  public class Demo1 {
  @Test
  public void test(){
  //设置可执行的驱动文件路径
  System.setProperty("webdriver.gecko.driver", "src/test/resources/geckodriver.exe");
  //创建火狐驱动对象
  WebDriver driver = new FirefoxDriver();
  //访问腾讯课堂首页
  driver.get("https://ke.qq.com/");
  //定位页面的搜索框
  WebElement searchInputBox = driver.findElement(By.id("js_keyword"));
  //往搜索框输入数据
  searchInputBox.sendKeys("柠檬班");
  //定位到搜索按钮
  WebElement searchButton = driver.findElement(By.id("js_search"));
  //点击搜索
  searchButton.click();
  }
  }
  驱动文件位置:
  需求很简单,代码也很简单。但是你知道代码中的这些浏览器操作,元素操作,是如何完成的吗?
  比如,浏览器启动完成后,再调用:driver.get("https://ke.qq.com/");就能在导航栏中访问到指定的这个页面,这个里面发生了什么,到底是客户端脚本直接操作浏览器还是通过某些中间件来完成??
  底层原理如下:
  1.在自动化测试过程中,存在三部分组件:客户端脚本+驱动+浏览器终端。
  2.驱动文件,以geckodriver.exe为例,这个可执行的驱动文件启动后,相当于一个暴露了一系列接口的服务器,监听某一端口,例如:89890。
  3.客户端的操作(访问页面,定位元素,输入数据,点击按钮等)都是封装成了接口请求(eg:/session/xx/yy),然后提交到驱动服务器。
  4.驱动服务器接收到客户端的请求后,再跟终端浏览器交互。
  5.终端浏览器做出相应操作。
  下图描述了整个交互过程:
  以定位元素为例,定位搜索框,我们来看底下这行代码在执行的时候底层到底经历了些什么:
 WebElement searchInputBox = driver.findElement(By.id("js_keyword"));
  
    实际,底层请求时,每个请求会被封装为一个command,然后根据不同的commannd封装得到不同的HttpRequest对象:
  根据此命令,得到接口地址:
  拿到此接口地址封装为一个HttpRequest请求。
  client.execute(httpRequest,true),执行接口调用:
  至于其他操作:往输入框输入数据,点击按钮等,都是对应一个接口地址,通过调用接口,请求驱动来处理,最后驱动同浏览器进行交互,浏览器按照指示做出对应操作。
  Selenium有一个类AbstractHttpCommandCodec,此类中维护了众多自动化操作对应的接口地址:
   public AbstractHttpCommandCodec() {
  defineCommand(STATUS, get("/status"));
  defineCommand(GET_ALL_SESSIONS, get("/sessions"));
  defineCommand(NEW_SESSION, post("/session"));
  defineCommand(GET_CAPABILITIES, get("/session/:sessionId"));
  defineCommand(QUIT, delete("/session/:sessionId"));
  defineCommand(GET_SESSION_LOGS, post("/logs"));
  defineCommand(GET_LOG, post("/session/:sessionId/log"));
  defineCommand(GET_AVAILABLE_LOG_TYPES, get("/session/:sessionId/log/types"));
  defineCommand(SWITCH_TO_FRAME, post("/session/:sessionId/frame"));
  defineCommand(SWITCH_TO_PARENT_FRAME, post("/session/:sessionId/frame/parent"));
  defineCommand(CLOSE, delete("/session/:sessionId/window"));
  defineCommand(SWITCH_TO_WINDOW, post("/session/:sessionId/window"));
  defineCommand(FULLSCREEN_CURRENT_WINDOW, post("/session/:sessionId/window/fullscreen"));
  defineCommand(GET_CURRENT_URL, get("/session/:sessionId/url"));
  defineCommand(GET, post("/session/:sessionId/url"));
  defineCommand(GO_BACK, post("/session/:sessionId/back"));
  defineCommand(GO_FORWARD, post("/session/:sessionId/forward"));
  defineCommand(REFRESH, post("/session/:sessionId/refresh"));
  defineCommand(SET_ALERT_CREDENTIALS, post("/session/:sessionId/alert/credentials"));
  defineCommand(UPLOAD_FILE, post("/session/:sessionId/file"));
  defineCommand(SCREENSHOT, get("/session/:sessionId/screenshot"));
  defineCommand(ELEMENT_SCREENSHOT, get("/session/:sessionId/screenshot/:id"));
  defineCommand(GET_TITLE, get("/session/:sessionId/title"));
  defineCommand(FIND_ELEMENT, post("/session/:sessionId/element"));
  defineCommand(FIND_ELEMENTS, post("/session/:sessionId/elements"));
  defineCommand(GET_ELEMENT_PROPERTY, get("/session/:sessionId/element/:id/property/:name"));
  defineCommand(CLICK_ELEMENT, post("/session/:sessionId/element/:id/click"));
  defineCommand(CLEAR_ELEMENT, post("/session/:sessionId/element/:id/clear"));
  defineCommand(
  GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
  get("/session/:sessionId/element/:id/css/:propertyName"));
  defineCommand(FIND_CHILD_ELEMENT, post("/session/:sessionId/element/:id/element"));
  defineCommand(FIND_CHILD_ELEMENTS, post("/session/:sessionId/element/:id/elements"));
  defineCommand(IS_ELEMENT_ENABLED, get("/session/:sessionId/element/:id/enabled"));
  defineCommand(ELEMENT_EQUALS, get("/session/:sessionId/element/:id/equals/:other"));
  defineCommand(GET_ELEMENT_RECT, get("/session/:sessionId/element/:id/rect"));
  defineCommand(GET_ELEMENT_LOCATION, get("/session/:sessionId/element/:id/location"));
  defineCommand(GET_ELEMENT_TAG_NAME, get("/session/:sessionId/element/:id/name"));
  defineCommand(IS_ELEMENT_SELECTED, get("/session/:sessionId/element/:id/selected"));
  defineCommand(GET_ELEMENT_SIZE, get("/session/:sessionId/element/:id/size"));
  defineCommand(GET_ELEMENT_TEXT, get("/session/:sessionId/element/:id/text"));
  defineCommand(SEND_KEYS_TO_ELEMENT, post("/session/:sessionId/element/:id/value"));
  defineCommand(GET_ALL_COOKIES, get("/session/:sessionId/cookie"));
  defineCommand(GET_COOKIE, get("/session/:sessionId/cookie/:name"));
  defineCommand(ADD_COOKIE, post("/session/:sessionId/cookie"));
  defineCommand(DELETE_ALL_COOKIES, delete("/session/:sessionId/cookie"));
  defineCommand(DELETE_COOKIE, delete("/session/:sessionId/cookie/:name"));
  defineCommand(SET_TIMEOUT, post("/session/:sessionId/timeouts"));
  defineCommand(SET_SCRIPT_TIMEOUT, post("/session/:sessionId/timeouts/async_script"));
  defineCommand(IMPLICITLY_WAIT, post("/session/:sessionId/timeouts/implicit_wait"));
  defineCommand(GET_APP_CACHE_STATUS, get("/session/:sessionId/application_cache/status"));
  defineCommand(IS_BROWSER_ONLINE, get("/session/:sessionId/browser_connection"));
  defineCommand(SET_BROWSER_ONLINE, post("/session/:sessionId/browser_connection"));
  defineCommand(GET_LOCATION, get("/session/:sessionId/location"));
  defineCommand(SET_LOCATION, post("/session/:sessionId/location"));
  defineCommand(GET_SCREEN_ORIENTATION, get("/session/:sessionId/orientation"));
  defineCommand(SET_SCREEN_ORIENTATION, post("/session/:sessionId/orientation"));
  defineCommand(GET_SCREEN_ROTATION, get("/session/:sessionId/rotation"));
  defineCommand(SET_SCREEN_ROTATION, post("/session/:sessionId/rotation"));
  defineCommand(IME_GET_AVAILABLE_ENGINES, get("/session/:sessionId/ime/available_engines"));
  defineCommand(IME_GET_ACTIVE_ENGINE, get("/session/:sessionId/ime/active_engine"));
  defineCommand(IME_IS_ACTIVATED, get("/session/:sessionId/ime/activated"));
  defineCommand(IME_DEACTIVATE, post("/session/:sessionId/ime/deactivate"));
  defineCommand(IME_ACTIVATE_ENGINE, post("/session/:sessionId/ime/activate"));
  // Mobile Spec
  defineCommand(GET_NETWORK_CONNECTION, get("/session/:sessionId/network_connection"));
  defineCommand(SET_NETWORK_CONNECTION, post("/session/:sessionId/network_connection"));
  defineCommand(SWITCH_TO_CONTEXT, post("/session/:sessionId/context"));
  defineCommand(GET_CURRENT_CONTEXT_HANDLE, get("/session/:sessionId/context"));
  defineCommand(GET_CONTEXT_HANDLES, get("/session/:sessionId/contexts"));
  }
  另外,可能会有人好奇,驱动服务器是何时启动的服务。其实是在执行下面这行代码的时候启动的,大家可执行去debug调试selenium的底层代码:
   //创建火狐驱动对象
  WebDriver driver = new FirefoxDriver();
  当上面这行代码执行完,可以发现eclipse的控制台显示了如下信息:
   1531911173760    geckodriver    INFO    geckodriver 0.19.0
  1531911173772    geckodriver    INFO    Listening on 127.0.0.1:21984
  说明此驱动服务器成功启动了,并且监听了本机的21984端口,等待客户端发起请求,并处理。

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号