execute
, 当把 bookshop.txt 作为参数来调用它时,会有什么情况发生呢 ? 聪明的读者可能已经猜到了,那就是 user 和 pwd 的值会被打印出来。这段代码展示了 Ruby 语言的两个重要特点 :
instance_eval
方法会把一段文本当做代码来执行。执行的上下文就是对象BookshopDSLBuilder
。 所以当它碰到文本 "login" 时,会自动调用真正的方法login
。- 在调用一个方法时,可以不加括号。这就是为什么 Ruby 会把文本
login "andy","pass4you"
当做一个方法调用的原因。
这两个特点就给我们搭了一座“桥”,使得我们可以把那个面向业务测试的文本诸如“login”,“add_to_cart”,“search_book”等转化为对特定方法的调用了。我们就可以在这些方法中实现某些逻辑。
我们现在已经能够把业务测试的脚本和 Ruby 的对象 / 方法连接起来,可是还需要第二座桥把 Ruby 和 Web 应用程序连接起来,这样才能使业务测试的脚本驱动 Web 页面进行测试。我们希望能有一个软件或工具可以像人一样来驱动浏览器的操作,例如点击链接,填充表单,点击按钮等等。当然它也可以检查页面的结果,例如期待的文本是否出现等。
开源工具 Watir 就是这样一个工具,除了具备上述功能外,它和 Ruby 语言还能进行无缝的集成,并且对浏览器尤其是 IE 有超强的控制能力。所以我们选取它作为第二座桥。
下面是一个使用 watir 的简单例子,它进入 Google 的首页,在搜索框中键入 "bookshop", 然后点击"搜索"按钮。 Watir 充分继承了 Ruby 语言简单明了的特点,读者可以看到使用 Watir 的脚本是相当直观,相当容易的。
清单 2. Watir 例子
require "watir" ie = Watir::IE.new ie.goto "http://www.google.com" ie.text_field(:name, "q").set "bookshop" ie.button(:name, "btnG").click |
有了上面的两座“桥”,具体的实现就简单多了,对于每一个业务操作,我们需要做的是 :
(1) 在一个 Ruby 对象中 (BookshopDSLBuilder) 实现一个同名的方法
(2) 在方法实现中,利用 watir 来操作界面元素。当然前提是我们需要知道界面上有哪些元素。
先来看一看 Login 的实现:
清单 3. Login
class BookshopDSLBuilder include Test::Unit::Assertions #include ruby unit 的 Assertion def self.execute( dsl) builder=new builder.instance_eval(File.read(dsl), dsl) builder end def initialize @login_url = 'http://localhost:3000/bookshop/login' #51Book 的入口 #creat a ie instance @ie= Watir::IE.new # 创建一个 Watir 的实例 end def login(user=nil,pwd=nil) @ie.goto @login_url @ie.text_field(:id,"user_name").set(user) # 设置用户名 @ie.text_field(:id,"user_password").set(pwd) # 设置密码 @ie.button(:type,"submit").click # 点击提交按钮 end end |
把书籍添加的购物车中这个操作相对复杂,因为它接收的参数是一个书籍的标题,而在界面上"Add to Cart"却是一个只包含 book id, 不包含标题的链接,所以无法直接定位。
清单 4. Add to Cart
<table width='100%' class='book'> <tr> <td>title:</td> <td>Agile development</td> # 标题在这里 </tr> <tr> <td>description:</td> <td>The book of agile development</td> </tr> <tr> <td>price:</td> <td>30.0</td> </tr> <tr> <td colspan="2"> #Add_To_Cart Link 却在这里 <a href='/bookshop/add_to_cart/1' >Add to Cart</a> </td> </tr> </table> |
这种情况下就可以利用 Watir 对 xpath 强大的支持,先找到标题,在从标题找到链接,最后点击链接即可。
清单 5. 使用 XPath
def add_to_cart(title) table = @ie.table(:xpath, "//table[@class='book']/tbody/tr/td[text()='"+title+"']/../../../") if table[1][2].text == title href = table[4][1].links[1].href @ie.link(:href,href).click end end |
对于其他的业务操作,具体的实现方式也是大同小异,这里不再一一介绍,有兴趣的读者可以参见 附件 中的代码,最后我们来看一个面向业务的 Web 页面测试例子:
清单 6. 一个完整的例子
login 'andy','pass4you' add_to_cart 'Agile development' add_to_cart 'Savor Blue' add_to_cart 'Programming Ruby' change_quantity 'Agile development',10 change_quantity 'Savor Blue',10 change_quantity 'Programming Ruby',10 recalculate_cart assert_total_price_is 900 search_book 'Ant cookbook' add_to_cart 'Ant cookbook' assert_total_price_is 910 |
到目前为止,我们已经通过 Ruby 完整的实现了“业务驱动” 的 Web 应用测试,实际上我们通过 Ruby 实现了一个面向业务的抽象层,利用 Watir 把业务操作映射到了对 Html 页面的操作。这样当 Html 页面发生了变化的时候,只需要调整映射,而不需要更改业务层的操作。同时由于它们是完全面向业务的,就使得开发人员或测试人员能把精力集中到业务逻辑的测试上,而不用陷入实现的细节。
掌握了该方法以后,读者可以应用到自己的程序中,可以使得自己的测试编写简单,容易理解,易于维护。将会极大的提供 Web 应用的测试效率。(责任编辑:A6)