应用Selenium和Ruby进行面向领域的Web测试[1] 软件测试
应用Selenium进行Web测试时,经常会遇到下面的几个麻烦问题:
大量使用name、id、xpath等页面元素。无论是功能修改、UI重构还是交互性改进都会影响到这些元素,这使得Selenium测试变得非常脆弱。
过于细节的页面操作不容易体现出行为的意图,一段时间之后就很难真正把握测试原有的目的了,这使得Selenium测试变得难于维护
对具体数据取值的存在依赖,当个别数据不再合法的时候,测试就会失败,但这样的失败并不能标识功能的缺失,这使得Selenium测试变得脆弱且难以维护。
而这几点直接衍生的结果就是不断地添加新的测试,而极少地去重构、利用原有测试。其实这倒也是正常,单元测试测试写多了,也有会有这样的问题。不过比较要命的是,Selenium的执行速度比较慢(相对单元测试),随着测试逐渐的增多,运行时间会逐渐增加到不可忍受的程度。一组意图不明而且难以维护的Selenium测试,可以很轻松地在每次构建(Build)的时候杀掉40分钟甚至2个小时的时间,我就有曾有花2个小时坐在电脑前面等待450个 Selenium测试运行通过的悲惨经历。因此合理有效地规划Selenium测试就显得格外的迫切和重要了。而目前比较行之有效的办法,往大了说,可以叫基于领域的Web测试(Domain Based Web Testing),具体来讲,就是Page Object Pattern。
Page Object Pattern里有四个基本概念:Driver、Page、Navigator和Shortcut等。Driver是测试真正的实现机制,比如 Selenium,比如Watir,比如HttpUnit。它们懂得如何去真正执行一个Web行为,通常包含像Click、Select、Type等这样的表示具体行为的方法;Page是对一个具体页面的封装,它们了解页面的结构,知道诸如id、name、class和xpath这类实现细节,并描述用户可以在其上进行何种操作;Navigator则代表了URL,表示一些不经页面操作的直接跳转;最后Shortcut就是helper方法了,需要看具体的需要而定。下面来看一个超级简单的例子——测试登录页面。
1. Page Object
假设我们使用一个单独的登录页面进行登录,那么可能会将登录的操作封装在一个名为LoginPage的page object里:
class LoginPage
def initialize driver
@driver = driver
end
def login_as user
@driver.type 'id=', user[:name]
@driver.type 'xpath=', user[:password]
@driver.click 'name='
@driver.wait_for_page_to_load
end
end
login_as是一个具有业务含义的页面行为。在login_as方法中,page object负责通过依靠id、xpath、name等信息完成登录操作。在测试中,我们可以这样来使用这个page object: