范尼是德鲁伊

matthew的技术博客

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

0.问题

  今天看到公司的同事们在讨论page object的相关知识(也就是面向Web项目的自动化测试中的一种实现形式),刚好前一阵子研究了一下我们项目组使用的page object相关内容,所以在这里写篇入门级别的文章,跟大家介绍一下gizmo这个小工具。

(大家可以先通过这篇文章看看page object的概念 http://www.infoq.com/cn/articles/domain-web-testing)

  说起来我研究gizmo也是有一段故事的,那天在工作时突然发现项目中一段代码是这样的

on_page_with(:mega_menu) do |mega_menu|
mega_menu.should have_single_word_space_caption(spaces.hashes)
end

  其中调用了 have_single_word_space_caption这个方法,但是我却发现方法是这样定义的

def has_single_word_space_caption? spaces
...
end

  这不是很奇怪么,定义的时候明明是'has',调用时候却使用'have'...,于是我又找了找,发现了更奇怪的方法调用

on_page_with(:mega_menu) do |mega_menu|
mega_menu.should have_space_title(space_name)
end

def has_space_title? space_name
end



  这个更离谱,have变has不说,连方法名最后的问号都没有了。自己想了半天都没想通,又问了问别人,也没有得到让我满意的答案,哎,没办法,只能踏上寻宝之路了。这段代码是我们利用gizmo,一个Ruby gem,来完成的page object模型,也就是说我们利用gizmo对我们项目中出现的网页都建立了模型,并且给这些网页模型中加入了相关方法。想找到为什么have变has,那么就先来google gizmo吧。(我会默认你接触cucumber测试,rspec,capybara等,就算你没有,我想理解下面的问题也不会很难的。)

 

1.gizmo简介

  gizmo(https://github.com/icaruswings/gizmo)一个page object testing framework,秉承ruby中那种简单随意的风格,利用它,你很快就可以建立起一套合适的page object来。


2.gizmo module

  首先介绍的就是gizmo module ,gizmo认为一个module就是在网页上面能独立出现的一块元素,比如淘宝最上方永远都出现的登陆窗口,或者是最下面的联系我们等。我个人认为,一个页面总是可以分解成不同的module。

  简单来说,gizmo的module需要满足以下条件:

  • module名字必须以PageWith开头,如下面的PageWithGithubSearch,module的文件名必须是page_with开头,如page_with_github_search.rb
  • 必须 include Gizmo::PageMixin
# I'm gizmo module
module PageWithGithubSearch
include Gizmo::PageMixin
end

 

  对于一个module来说,我个人建议最好不要将module当成是一个页面。因为某些网页中可能存在相同的块,比如我上面提到的淘宝的例子,如果我们将相同的内容抽成module,就像抽取方法一样,项目中就不会有重复的代码啦~~

 

3.gizmo method

  有了module,我们该定义怎样的方法来满足我们的使用呢?总体来说,我们一般会用到四类方法

  • 判断页面是否加载正常
  • 与网页有所交互,比如点击按钮,输入文字等
  • 判断页面某元素是否存在或者不存在
  • 读取网页上面的信息
module PageWithGithubSearch

include Gizmo::PageMixin

def valid?
has_selector?("form[action='/search']")
end

def link_with_text(value)
find("a", :text => value)
end

def have_content(value)
has_content?(value)
end

define_action :click_element do |element|
locate(element).click
end
end

  

  上述代码中,valid?方法就是判断页面是否加载正确(准确的说是页面中的某一个独立的块是否加载正确),你可以直接使用capybara的方法来帮助你判断加载是否成功(capybara, https://github.com/jnicklas/capybara)。如果某些情况你不需要验证是否加载正确,那么就不需要valid?方法,gizmo默认的valid?方法会永远返回true。

  define_action能帮助我们定义与页面上进行交互的方法,后面的:click_element是方法名,block中的参数是方法的参数。实现依然是capybara的代码,虽然在定义时我们使用了define_action(gizmo自已的dsl),但实际上运行时它还是会定义一个方法,之所以引入define_action的概念,个人觉得是可以非常方便的区分每个方法的职责,如果你需要与界面交互的方法,那就用define_action吧。

  link_with_text方法帮我们读取页面中的text值是value的a标签并且返回该标签,实际上find方法还是capybara的方法。

  have_content方法能判断该页面中是否包含某段文字。

  通过上面的介绍,我们就可以用valid?,define_action,def 这三种形式完成对module行为的定义。而在实现时,使用capybara来完成具体的操作(上述例子比较粗糙,希望大家在实际中能更多的参考capybara文档)。有人问我能否全部用def,不用define_action来定义方法,我觉得从功能来说完全可以,但是既然使用gizmo,那么就老老实实遵守规定吧。

 

4.gizom call 

  总算定义完module了,那么我们该怎么用呢?其实很简单,直接上代码:

on_page_with :github_search do |page|
page.perform :search, query
end

on_page_with :github_search do |page|
page.should have_content("github")
end

  所有使用gizmo的时候都需要on_page_with方法,第一个例子中后面传入的:github_search是module的名字,注意需要去掉前面的PageWith并且全部小写,单词之间需要下划线,还要在前面加上冒号,比如PageWithGithubSearch就是:github_search。后面是你需要操作的block,如果我们需要调用define_action定义的方法,需要使用perform方法,然后传入的:search是方法名,后面跟方法的参数query.

  在第二个例子中,我们使用rspec中的should来判断 have_content("github")的返回值,也就是判断页面上是否包含github字符串。(不知你发现了没有,我定义的时候是方法名是has开头,调用时使用have并且去掉了'?')

  简单来说,on_page_with后面跟的就是使用module的名字,如果我们需要操作页面,就是用perform方法,如果需要进行某些判断,则使用rspec来完成。

 

5.configuration

  对于使用者来说,最麻烦的就是配置问题了。我在gizmo主页上也没有找到很详细的配置文档,所以就给大家分享一下我的配置内容吧。

#features/support/env.rb
require 'capybara'
require 'capybara/dsl'
require 'capybara/cucumber'
require 'gizmo'

Capybara.default_driver = :selenium
Capybara.default_wait_time = 10
Capybara.default_selector= :css
Capybara::Selenium::Driver::DEFAULT_OPTIONS[:resynchronize] = false
Capybara.ignore_hidden_elements = false


World(Capybara)
World(Gizmo::Helpers)

Gizmo.configure do |config|
config.mixin_dir = File.dirname(__FILE__) + '../page_object'
end

 

  值得说明的最后一部分,设置Gizmo.configure中设置的是存放gizmo module的文件夹,他是相对于env的路径,也就是说我可以把放gizmo module的文件夹叫做pages,并且把相关的module放在一个子文件夹下面进行管理。比如下面这种组织形式  

page_object/
├── home_page
│ ├── page_with_login.rb
│ └── page_with_search.rb
└── setting_page
├── page_with_password.rb
└── page_with_photo.rb

  最后,你需要参照 https://github.com/icaruswings/gizmo/wiki/Capybara,给你的capybara打上补丁才能工作。

  那么,如果你有什么问题,可以给我留言,不过我建议你最好仔细看一看gizmo主页上面的介绍。那么,还等什么,开始page object之旅吧。

ps:到现在为止,我依然没有确定have和has这个问题的答案,如果你知道,请你告诉我,谢谢。  

posted on 2012-02-22 11:16  范尼是德鲁伊  阅读(1059)  评论(1编辑  收藏  举报