用于运行代码两次的Ruby习惯用法(“中间代码”)

时间:2011-02-01 19:31:15

标签: ruby

在我的Capybara + Webdriver AJAX测试中,我看到了这样的代码模式:

page.should have_selector('foo.bar > baz')  # added dynamically by JS
visit current_page
page.should have_selector('foo.bar > baz')  # still there after reload

我将其解压缩为persist辅助函数,该函数执行

def persist
  yield
  visit current_page
  yield
end

问题:在没有帮助函数的情况下,是否有一个紧凑的成语来内联相同的东西?

我能想到的最短的是

2.times { |i|
    page.should have_selector('foo.bar > baz')
    visit current_page if i == 0
}

这是干的,但仍然很难看。

编辑:我认为Mark的评论非常正确,而且我坚持使用persist帮助器来处理这个特殊用例。也就是说,下面的答案中有几个好的(和有趣的)想法。

编辑2:如果有人想要复制我的persist示例:使用RSpec,在@__memoized = {}之后添加visit current_page以便更新是有用的任何let个持有节点,在页面重新加载后会过时(否则你会得到一个ObsoleteElementError)。

3 个答案:

答案 0 :(得分:5)

在我看来,重复一行是没有错的,如果它使代码更具可读性。您的案例似乎是这种合理重复的一个很好的例子:)

如果你发现自己重复了很多模式,你可能希望用{/ p>这样的方式扩展Object

def should_still(predicate)
  should predicate
  yield
  should predicate
end

然后你可以编写像

这样的紧凑语句
page.should_still have_selector('foo.bar > baz') { visit current_page }

答案 1 :(得分:3)

您可以按照http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/1052c289b22c60a5

的模式制作通用版本
class AroundWrapper
  def initialize &block
    (class << self
      def self.outer &block
        define_method :outer, &block
      end
      def self.inner &block
        define_method :inner, &block
      end
      self
    end).class_eval &block
  end
end

def around &block
  around_wrapper = AroundWrapper.new &block
  around_wrapper.outer
  around_wrapper.inner
  around_wrapper.outer
end

然后,这个:

around {
  outer { puts "Hello" }
  inner { puts "World" }
}

将产生此输出:

Hello
World
Hello

编辑:实际上,现在我已经想到了,这里有一个更简单的方法,在使用中读得非常好:

def around(inner)
  yield
  inner.call
  yield
end

around(lambda{puts "World"}) do
  puts "Hello"
end

答案 2 :(得分:0)

在这种情况下,我会引导你远离“聪明”的解决方案。测试应该是相当程序化的配方,如果你抽象成像你的例子那样,测试就失去了它的可读性。我可以很容易地理解第一次测试的意图和目的,但“浓缩”版本就像泥浆一样清晰。

在这种情况下,我鼓励您不一定觉得您需要避免代码重复,如果它让您清晰明了。如果您仍想抽象它,我会推荐像帮手一样的东西,比如

after_refresh { page.should have_selector("foo.bar > baz") }

然后,

def after_refresh(&block)
  yield
  visit current_page
  yield
end

这样可以保持测试的富有表现力,如果您正在测试很多这些刷新案例,那么可以避免重复。