WebDriver设计
WebDriver的API被定位为“基于对象的”。接口被明确定义并努力坚持只包含一个角色或者责任,而不是将每一个可能的HTML标记模块化为单独的类,只有一个WebElement接口。通过这种方式,开发人员使用支持自动补全的IDE即可被提示下一步工作。其结果类似于下面的代码片段(Java语言):
WebDriver driver = new FirefoxDriver();
driver.<user hits space>
此时,包含13个方法的短列表显示出来,用户选择其中一个:
driver.findElement(<user hits space>)
大多数IDE现在显示预期参数的类型提示,在这个例子中是“By”。By包含许多预定义的静态工厂方法,用户可以快速地完成刚才的代码:
driver.findElement(By.id("some_id"));
基于角色的接口
WebDriver广泛使用了基于角色的接口。例如,有一个JavascriptExecutor接口提供了在当前页面环境中执行任意Javascript语句块的功能。WebDriver实例对该接口的成功映射可以提示你利用该方法完成自己的工作。
处理大量的组合
假设X种浏览器和Y种语言,我们很容易会掉进X×Y种实现中,将会面临维护成本的大量攀升。
减少WebDriver支持的编程语言种类是降低成本的途径之一,但是基于两种原因不想这样做。首先,从一种语言切换到另一种时人们会承受认知负荷,因此对用户来说如果测试框架(WebDriver)能够允许他们采用在日常开发中使用的编程语言来编写测试,那么这是巨大的优势。其次,在单一项目中混合多种语言可能会让团队很不方便,而且公司的编码规范和需求通常要求技术单一纯正性,因此,减少支持语言的种类不是可选项。
减少支持浏览器的数量也不是一种选择——当项目组决定在WebDriver中淘汰对Firefox 2的支持时,遇到了强烈的抗议,而事实上当时,Firefox 2只占了浏览器市场份额不到1%。
的选择是努力使所有浏览器对语言的绑定看起来相同:它们应该提供统一的接口,可以轻松地通过各种语言解决。更重要的是,希望语言绑定本身尽可能的易于编写,这意味着需要尽可能的使它们保持简洁。在底层driver中放入了尽可能多的逻辑来支持这种设计:放弃放入dirver的每一块功能都意味着需要通过支持的每一种语言实现,而这代表了大量的工作量。
WebDriver设计中的缺陷
通过这种方式发布功能的缺陷在于除非有人知道某个特定的接口存在,否则他们可能不会意识到WebDriver支持这种功能,在API的可发掘性上存在缺憾。当然在WebDriver刚发布的时候,我们会投入大量时间来指导人们找到合适的接口。现在我们已经花费大量精力来编写文档,随着API获得广泛应用,用户会越来越容易的找到所需的文档。
从实现者的观点来看,紧密绑定浏览器也是一种设计缺陷,虽然无法避免。支持新浏览器时需要投入巨大的努力,经常需要数次尝试才能找到正确方法。具体的例子是,Chrome驱动经过了四次完全重写,IE驱动也有三种关键重写。紧密绑定浏览器的优点在于它提供了更多控制权。
远程Webdriver协议的第二次迭代因此使用了HTTP传输机制和UTF-8编码的JSON作为默认的编码,使客户端可以很容易的使用各种有限支持Unicode的语言,因为UTF-8向后兼容ASCII。发送给服务器的命令使用URL以确定哪个命令被发送,并编码在一个数组中的命令的参数。
例如,一个命令WebDriver.get("http://www.example.com")映射到一个到URL的POST请求,编码了回话ID和以”/”结束,使用像{[}'http://www.example.com'{]}这样的参数的阵列。返回的结果更有结构性,有一个返回值和错误码的代码占位。不久到了第三次远程协议迭代,使用命名参数字典取代了参数请求数组。这有利于更方便的调试请求,消除了客户端失误使用了参数的可能性,使得系统更健壮。当然,决定使用正常的HTTP错误码表示确定的返回值和响应,例如,如果用户试图调用一个没有映射到的URL,或者当我们想表示空响应时,这是种合适的方式。
Webdriver远程协议有两个错误处理层次,一个无效的请求和一个失败的命令。无效的请求的例子是请求一个服务器并不存在的资源,或者可能是资源不理解,(例如发送一个DELETE命令给用于处理当前页面的URL的资源)。这种情况下,一个正常的HTTP响应是4XX。对于失败的命令,错误码设置为500(服务器端错误),并且返回的数据包含更详细的错误分解。
当包含数据的响应从服务器发出后,它是一个JSON对象的格式:
Key 描述
Sessionid 服务器使用的决定何处路由特定会话命令的不透明处理
状态值 一个概括命令结果数字状态码,非零值代表命令失败响应的JSON值
例如:
FirefoxDriver.prototype.getElementAttribute =
function(respond, parameters) {
var element = Utils.getElementAt(parameters.id, respond.session.getDocument());
var attributeName = parameters.name;
respond.value = webdriver.element.getAttribute(element, attributeName);
respond.send();
};
因为可见,我们会在响应中编码状态码,使用非零值表示发生了糟糕的事故。首先在IE driver上使用了状态码,而且被用于了线协议。因为所有的错误码在不同的driver中一致的,在不同的driver中可以使用特殊的语言完成共享错误处理的代码,使客户端更容易实现。
远程webdriver服务简单讲是一个Java servlet,扮演多重角色,路由收到的所有的命令给对应的Webdriver实例。诸如此类的事情,一个大二的学生都能完成。Firefox driver也完成了远程Webdriver协议,它的架构更加有趣,所以让我们跟踪一个从语言绑定的会话到后端一直到返回给用户的请求。