页面对象模型,这些方法有多离散?

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!')

如果有人决定更改邮件的标点符号或大小写,将会失败并显示有用的消息。