如何在 Ruby 中创建和使用由字符串值动态命名的变量?

How to create and use variables dynamically named by string values in Ruby?

我正在使用 SitePrism 创建一些 POM 测试。我的其中一个页面 类 如下所示:

class HomePage < SitePrism::Page
    set_url '/index.html'
    element :red_colour_cell, "div[id='colour-cell-red']"
    element :green_colour_cell, "div[id='colour-cell-green']"
    element :blue_colour_cell, "div[id='colour-cell-blue']"

    def click_colour_cell(colour)
        case colour
            when 'red'
                has_red_colour_cell?
                red_colour_cell.click
            when 'green'
                has_green_colour_cell?
                green_colour_cell.click
            when 'blue'
                has_blue_colour_cell?
                blue_colour_cell.click
        end
    end
end

方法 click_colour_cell() 获取从调用此方法的水豚测试步骤传递的字符串值。 如果我将来需要创建其他类似的方法,那么使用如此多的大小写切换来确定代码流可能会变得相当乏味和笨拙。

有什么方法可以创建一个由另一个变量的字符串值动态命名的变量吗?例如,我想为 click_colour_cell() 执行类似于以下内容的操作:

    def click_colour_cell(colour)
        has_@colour_colour_cell?
        @colour_colour_cell.click
    end

其中 @colour 表示传递值的值,colour 并会被 Ruby 解释:

    def click_colour_cell('blue')
        has_blue_colour_cell?
        blue_colour_cell.click
    end

这不是实例变量的作用吗?我已尝试将上述建议作为解决方案,但收到不明确的错误:

syntax error, unexpected end, expecting ':'
    end
    ^~~ (SyntaxError)

如果它是我需要使用的实例变量,那么我不确定我是否正确使用了它。如果还有什么需要用的,请指教。

实例变量用于定义对象的属性。

相反你可以通过方法send和字符串插值来实现。

试试下面的方法:

def click_colour_cell(colour)
  send("has_#{colour}_colour_cell?")
  send("#{colour}_colour_cell").click
end

关于发送:

sendObject class 中定义的方法(所有 class 的父 class)。

正如 documentation 所说,它调用由给定字符串或符号标识的方法。您还可以将参数传递给您尝试调用的方法。

在下面的代码片段中,send 将搜索名为 testing 的方法并调用它。

class SendTest
  def testing
    puts 'Hey there!'
  end
end


obj = SendTest.new
obj.send("testing")
obj.send(:testing)

输出

Hey there!
Hey there!

在你的例子中,考虑传递给 colour 的参数是 blue,

"has_#{colour}_colour_cell?" 将 return 字符串 "has_blue_colour_cell?" 并发送将动态调用名为 has_blue_colour_cell? 的方法。方法 blue_colour_cell

也是如此

直接回答您的问题

您可以动态 get/set 实例变量:

instance_variable_get("@build_string_as_you_see_fit")
instance_variable_set("@build_string_as_you_see_fit", value_for_ivar)

但是...

警告!

我认为在这里 and/or 动态创建变量使用 string-building 方法名称到 send 是一个坏主意,会极大地阻碍未来的可维护性。

这样想:任何时候你看到这样的方法名称:

click_blue_button
click_red_button
click_green_button

这与做同样的事情:

add_one_to(1)   // instead of 1 + 1, i.e. 1.+(1)
add_two_to(1)   // instead of 1 + 2, i.e. 1.+(2)
add_three_to(1) // instead of 1 + 3, i.e. i.+(3)

您没有将有意义的参数传递到方法中,而是将 hard-coding 值传递到方法名称中!继续这个,最终你的整个代码库将不得不处理已经 hard-coded 到方法名称中的“值”。

更好的方法

您应该改为这样做:

class HomePage < SitePrism::Page
  set_url '/index.html'

  elements :color_cells, "div[id^='colour-cell-']"

  def click_cell(color)
    cell = color_cells.find_by(id: "colour-cell-#{color}") # just an example, I don't know how to do element queries in site-prism
    cell.click
  end
end

或者如果您必须将它们作为单独的元素:

class HomePage < SitePrism::Page
  set_url '/index.html'

  COLORS = %i[red green blue]

  COLORS.each do |color|
    element :"#{color}_colour_cell", "div[id='colour-cell-#{color}']"
  end

  def cell(color:)                 # every other usage should call this method instead
    @cells ||= COLORS.index_with do |color|
      send("#{color}_colour_cell") # do the dynamic `send` in only ONE place
    end
    @cells.fetch(color)
  end
end

home_page.cell(color: :red).click