原子化:如何
分类
这是我的第二篇技术文章。 再次声明,如果您对 Selenium 2 的内部工作原理感兴趣,请直接跳到其他内容。如果您对我们如何以及为何在项目上做出一些技术决策感兴趣,请继续阅读……。
我们把英勇的英雄们置于困境:他们决定编写一个代码共享库,供各种 webdriver 实现和 selenium core 使用,但这样做的要求似乎与实际发生的情况不符。
幸运的是,大约在同一时间,我们开始走这条路时,Google 开源了 Closure 编译器。这是一个 JavaScript 编译器,它将一组 JavaScript 文件作为输入,并输出 JavaScript。可以将其配置为将代码原封不动地传递到单个文件中,也可以积极地编译脚本,删除未使用的代码路径并尽可能缩小输出。Closure 编译器在许多 Google 产品中使用,因此我们知道它是可靠且一致的。
为了充分利用 Closure 编译器,我们正在使用 Closure 库编写原子。它不像其他一些 JS 库那样出名,但它很可靠,经过了充分的测试,并且正在积极开发中。它还具有易于使用的 JsUnit 扩展,这使得编写测试比其他情况下的任务简单得多,并且它具有易于使用的代码模块化机制。
因此,考虑到我们可以将单个 JavaScript 函数(及其依赖项)编译成最小化的 JS 片段,我们都准备好了,对吗?不完全是。
问题在于,原子是从两个具有不同世界观的框架中提取的。例如,Selenium 1 的 getAttribute (http://selenium.googlecode.com/svn/trunk/docs/api/java/com/thoughtworks/selenium/Selenium.html#getAttribute(java.lang.String)
) 方法仅返回特定属性的值,而 WebDriver 的 getAttribute (http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/WebElement.html#getAttribute(java.lang.String
)) 方法将返回属性或属性的值(因为有时很难记住某个东西是元素的属性还是属性)
与计算机科学中的所有问题一样,使用额外的间接级别来解决此问题。
我们正忙于在原子的基础上实现 WebDriver 和 Selenium API 的预期行为。
当然,还有一个明显的问题,即我们如何将这种精心压缩的 JS 放入驱动程序中。一种选择是将原始 JavaScript 作为文件包含在每种语言绑定中,并根据需要引入它们。这是可能的,但这会使每个语言绑定都过于臃肿,并会引入大量重复。另一种方法是将原子尽可能地推入驱动程序中,这就是我们所做的。作为 webdriver 构建过程的一部分,我们采用压缩的 JS 并将其转换为特定驱动程序可以使用的形式。例如,对于 IE 驱动程序,我们将它们转换为 C 头文件中的常量。然后,驱动程序可以引用这些常量,并将其转换回一个脚本,该脚本可以通过“executeScript”使用的相同机制来执行。
我们从这种看似巴洛克的做法中获得了什么?除了能够在驱动程序之间共享相同的代码之外?还有很多。维护成本急剧下降,因为我们可以在一个地方修复一个错误,并且每个驱动程序都会采用该修复。因为我们正在使用纯 JS 并仅查询 DOM,所以我们可以在开发代码时在浏览器中运行单元测试。这导致非常紧密的反馈循环。这也使得不熟悉该代码的开发人员更容易查看我们的工作方式,并向我们发送补丁(始终非常感谢!)。最后,我们可以确保结果的一致性。
好了,有什么问题吗?