页面对象模型,这些方法有多离散?
Page Object Model, how discrete are the methods?
所以我一直在一个新项目上实施 PoM,这将是我第一次这样做。我使用 Capybara 和 Rspec (Selenium) 来编写我的框架。
我一直 运行 关注的一件事是 "discrete" 我应该如何在我的页面对象 类 中创建方法。谈到这个,我看到两种思路:
让我们制作一个制作小部件的页面:
选项 1(一般化,更关注用户会做什么)
class WidgetPage
def click_widgets_tab
click_on('Widgets')
end
def create_widget_button
click_on('Add Widget')
end
def enter_widget_name(name)
fill_in 'Widget Name', with: name
end
def enter_widget_type(type)
fill_in 'Widget Type', with: type
end
def widget_success?
expect(page).to have_content('.alert', text: 'Widget Successfully created!')
end
end
(在上述情况下,"Fill in" 方法甚至可以结合使用
或选项 2:
class WidgetPage
def click_widgets_tab
widget_tab_link.click
end
def create_widget_button
widget_add_element.click
end
def enter_widget_name(name)
widget_form_name.fill_in(name)
end
def enter_widget_type(type)
widget_form_type.fill_in(type)
end
def widget_success?
widget_success_alert.has_text? 'Widget successfully created!'
end
private
def widget_add_element
find_button('Widget')
end
def widget_form_name
find_field('Widget Name')
end
def widget_form_type
find_field('Widget Type')
end
def widget_tab_link
find_link('Widgets')
end
def widget_success_alert
find(.alert, text: "Widget successfully created!")
end
end
我觉得我看到大多数页面对象教程都使用选项 2...但是看起来很多额外的代码很少 return 投资。如果在多种方法中使用 return 元素的方法对我来说很有意义……但不是对所有方法都适用。
就断言而言,选项 1 更有意义。但我也听说你不应该在页面对象中有断言。因此,仅使用一种方法 return 判断警报是否可见可能更有意义?仍然不确定处理该问题的最佳方法。
我认为这是一个公平的问题,其中很多确实归结为偏好,但是如果您根据自己的情况选择了错误的 "preference",那么您可能会陷入一些非常真实的陷阱,所以我觉得值得一谈。
- 部分意见:如果您的应用 flaky/volatile/prone 需要解决方法,请考虑在页面对象中使用更细化的方法,并在步骤定义中使用更少的 Capybara。在步骤定义中有数十个
page.element.click()
调用非常烦人,所有这些调用都需要更新,因为应用程序更改为需要在每次点击前悬停。如果该操作作为 page.clickElement()
包装在页面对象中,那么您只有一个地方可以进行该更新(或实施解决方法)。
- 主要观点:如果您将 Capybara 可以为每个元素执行的所有操作完全包装起来,您最终会得到一个难以使用的臃肿页面对象。如果 step defs 将在 90% 的时间内使用一组操作,我会将它们公开为显式方法,并让其余 10% 的边缘情况类型操作直接使用这些元素。
- 部分观点:请注意水豚的
click_on
/fill_in
方法限制您使用他们的定位器概念(名称、ID 或标签)。与任意 CSS 相比,我总是发现它的限制如此之大,以至于我避免使用它们。
- 较少意见:避免在页面对象中放置断言!页面对象是页面的抽象,而不是业务规则的测试。对于上面的示例,这两个选项都将失败并显示 "could not find element" 或 "false != true",这不是很有帮助。考虑从页面对象返回文本(无论它是什么),并在步骤 def.
中断言
类似
def get_widget_alert
find(.alert).get_text()
end
从步骤 def 调用
expect(widget_page.get_widget_alert).to eq('Widget Successfully created!')
如果有人决定更改邮件的标点符号或大小写,将会失败并显示有用的消息。
所以我一直在一个新项目上实施 PoM,这将是我第一次这样做。我使用 Capybara 和 Rspec (Selenium) 来编写我的框架。
我一直 运行 关注的一件事是 "discrete" 我应该如何在我的页面对象 类 中创建方法。谈到这个,我看到两种思路:
让我们制作一个制作小部件的页面:
选项 1(一般化,更关注用户会做什么)
class WidgetPage
def click_widgets_tab
click_on('Widgets')
end
def create_widget_button
click_on('Add Widget')
end
def enter_widget_name(name)
fill_in 'Widget Name', with: name
end
def enter_widget_type(type)
fill_in 'Widget Type', with: type
end
def widget_success?
expect(page).to have_content('.alert', text: 'Widget Successfully created!')
end
end
(在上述情况下,"Fill in" 方法甚至可以结合使用
或选项 2:
class WidgetPage
def click_widgets_tab
widget_tab_link.click
end
def create_widget_button
widget_add_element.click
end
def enter_widget_name(name)
widget_form_name.fill_in(name)
end
def enter_widget_type(type)
widget_form_type.fill_in(type)
end
def widget_success?
widget_success_alert.has_text? 'Widget successfully created!'
end
private
def widget_add_element
find_button('Widget')
end
def widget_form_name
find_field('Widget Name')
end
def widget_form_type
find_field('Widget Type')
end
def widget_tab_link
find_link('Widgets')
end
def widget_success_alert
find(.alert, text: "Widget successfully created!")
end
end
我觉得我看到大多数页面对象教程都使用选项 2...但是看起来很多额外的代码很少 return 投资。如果在多种方法中使用 return 元素的方法对我来说很有意义……但不是对所有方法都适用。
就断言而言,选项 1 更有意义。但我也听说你不应该在页面对象中有断言。因此,仅使用一种方法 return 判断警报是否可见可能更有意义?仍然不确定处理该问题的最佳方法。
我认为这是一个公平的问题,其中很多确实归结为偏好,但是如果您根据自己的情况选择了错误的 "preference",那么您可能会陷入一些非常真实的陷阱,所以我觉得值得一谈。
- 部分意见:如果您的应用 flaky/volatile/prone 需要解决方法,请考虑在页面对象中使用更细化的方法,并在步骤定义中使用更少的 Capybara。在步骤定义中有数十个
page.element.click()
调用非常烦人,所有这些调用都需要更新,因为应用程序更改为需要在每次点击前悬停。如果该操作作为page.clickElement()
包装在页面对象中,那么您只有一个地方可以进行该更新(或实施解决方法)。 - 主要观点:如果您将 Capybara 可以为每个元素执行的所有操作完全包装起来,您最终会得到一个难以使用的臃肿页面对象。如果 step defs 将在 90% 的时间内使用一组操作,我会将它们公开为显式方法,并让其余 10% 的边缘情况类型操作直接使用这些元素。
- 部分观点:请注意水豚的
click_on
/fill_in
方法限制您使用他们的定位器概念(名称、ID 或标签)。与任意 CSS 相比,我总是发现它的限制如此之大,以至于我避免使用它们。 - 较少意见:避免在页面对象中放置断言!页面对象是页面的抽象,而不是业务规则的测试。对于上面的示例,这两个选项都将失败并显示 "could not find element" 或 "false != true",这不是很有帮助。考虑从页面对象返回文本(无论它是什么),并在步骤 def. 中断言
类似
def get_widget_alert
find(.alert).get_text()
end
从步骤 def 调用
expect(widget_page.get_widget_alert).to eq('Widget Successfully created!')
如果有人决定更改邮件的标点符号或大小写,将会失败并显示有用的消息。