【问题标题】:Testing index view sort order in RSpec feature spec (Capybara)在 RSpec 功能规范(Capybara)中测试索引视图排序顺序
【发布时间】:2017-07-24 16:49:23
【问题描述】:

Rails 4.2 应用程序的索引视图在其标题处有一个带有排序链接的表。如果用户单击“E-mail”标题,记录将按 E-mail 排序,依此类推。单击排序链接时,将重新加载页面(使用类似q=email+asc 的查询字符串)。尚未使用 AJAX。

我已经编写了以下测试。它有效,但我相信应该有更好的方法来测试它。

it "sorts by e-mail when the 'E-mail' column header is clicked", :focus do
  visit "/admin/users"
  expected_id_order = User.order(:email).map(&:id)
  # Once the page is reloaded (the new sort order is applied), the "stale"
  # class will disappear. Note that due to Turbolinks, only the part of
  # the DOM that is modified by a request will have its elements replaced.
  page.execute_script("$('tr.user:first').addClass('stale')")
  within "thead" do
    click_link("E-mail")
  end
  # Force Capybara to wait until the page is reloaded
  expect(page).to have_no_selector("tr.user.stale")
  actual_id_order = page.body.scan(/<tr.+id="user_(.+)".*>/).flatten
  expect(actual_id_order).to eq(expected_id_order)
end

其他细节:

  • &lt;TR&gt; 元素的 DOM ID 包含其对应记录的 DB ID,例如 &lt;tr class="user" id="user_34"&gt;。使用正则表达式提取记录在页面中的显示顺序可能不是最佳解决方案。你能推荐一个更好的方法吗?
  • 我不喜欢 JavaScript hack(使用 JQuery 添加 stale 类,然后等到它消失以确保重新加载页面),但到目前为止我找不到另一种方法来确保 Capybara 等到页面重新加载(应用新的排序顺序)。你能推荐一个更好的方法吗?
  • 应用程序使用Devise,因此我们需要创建用户记录才能登录。请注意,为登录目的创建的用户不可避免地会出现在我们的测试数据中。

【问题讨论】:

    标签: ruby-on-rails ruby testing rspec capybara


    【解决方案1】:

    最简单的方法是使用您控制测试数据的事实,相应地分配字符串,然后使用正则表达式来测试用户实际看到的输出,而不是特定的标签和 ID。

    within_table('user_table') do # any method that will scope to the table
      expect(page).to have_text /user3@test\.com.+user2@test\.com.+user1@test\.com/ # initial expected ordering
      click_link("E-mail")
      expect(page).to have_text /user1@test\.com.+user2@test\.com.+user3@test\.com/ # sorted expected ordering
    end
    

    Capybara 会等待,不需要 JS hack,等等。

    【讨论】:

    • 没想到Capybara的顶级贡献者会给出答案,谢谢。这是非常简单易读的。您创建测试数据的方法是什么?如果一个页面可以通过 8 个不同的属性进行过滤,我们是否需要在测试中“手动”创建 3 条记录并设置它们的属性值(3*8 = 24 个属性值)或者是否有更简单的方法来使用@987654322 @?
    • @BrunoFacca 您使用的是 FactoryGirl,因此无需使用 create_list,只需创建您需要的 3 条记录,只需为每个测试指定需要为特定值的字段(本案例的电子邮件)。 create(:user, email: 'email3@test.com'; create(:user, email: 'email2@test.com'); ... 等。它们将按 id 升序创建,然后假设它们最初以 id 升序显示,您知道它们应该如何开始,以及应该如何结束。请注意,您也可以使用数组 %w(email3@...).each { |email| create(:user email: email) } 进行创建
    • 再次感谢。我忘了提一些事情(抱歉,我刚刚编辑了问题):应用程序使用了 Devise,所以我们需要创建一个用户记录才能登录。为登录目的创建的用户不可避免地会“污染”我们的测试数据。
    • @BrunoFacca 使用可用于排序的已知值创建登录用户,或者不用担心,因为在使用正则表达式验证排序时,它会作为.+ 的一部分被忽略在其他值之间排序。
    • 没有想过它会如何作为.+ 的一部分被忽略。谢谢!
    【解决方案2】:

    this blog post 汲取灵感,您可以做类似的事情

    # get the index of the first occurrence of each users' email address in the response body
    actual_order = User.order(:email).map { |user| page.body.index(user.email) }
    # should already be in sorted order
    expect(actual_order).to eq(actual_order.sort)
    

    至于必须等待 .stale 类被删除,我建议改为修改您的 html 以在您的 tha 标签上包含某种“这是当前排序的列”类.

    <a id="email-sort-link" class="sorted asc">E-Mail</a>
    

    然后你可以等到看到a#email-sort-link.sorted。通常不喜欢修改代码以方便测试,但这似乎很小,并且允许您对链接进行不同的样式设置以提醒用户当前的排序。

    【讨论】:

      【解决方案3】:

      我有一个特定的案例,我想使用共享示例。因此,我根据 Thomas Walpole 的回答创建了以下内容。

      def sort_order_regex(*sort_by_attributes)
        /#{User.order(sort_by_attributes)
               .map { |u| Regexp.quote(u.email) }
               .join(".+")}/
      end
      
      RSpec.shared_examples "sort link" do |link_text:, sort_by:|
        # Make sure your factory creates unique values for all sortable attributes
        before(:each) { FactoryGirl.create_list(:user, 3) }
      
        it "sorts by #{sort_by} when the #{link_text} link is clicked" do
          visit "/admin/users"
          # Check the record's order by matching the order of their e-mails (unique).
          initial_order = sort_order_regex(:first_name, :last_name)
          tested_order = sort_order_regex(sort_by)
          within_table "users_table" do
            expect(page).to have_text(initial_order)
            click_link(link_text, exact: false)
            expect(page).to have_text(tested_order)
          end
        end
      end
      
      describe "User CRUD", type: :feature, js: true do
        describe "sorting" do
          include_examples "sort link", link_text: "E-mail", sort_by: :email
          include_examples "sort link", link_text: "Role", sort_by: :role
          include_examples "sort link", link_text: "Country", sort_by: :country
          include_examples "sort link", link_text: "Quotes", sort_by: :quotes_count
        end
        #...
      end
      

      【讨论】:

        【解决方案4】:

        我们可以用 CSS 的东西来测试它。

        一个例子:

        |---------------------|------------------|
        |     username        |     email        |
        |---------------------|------------------|
        |     bot1            | bot1@mail.com    |
        |---------------------|------------------|
        |     bot2            | bot2@mail.com    |
        |---------------------|------------------|
        

        我们的 RSpec 可以是:

        RSpec.describe "returns expected sorted row data" do
          it 'returns expected sorted row data' do
            # the headers column name
            within 'tr:nth-child(1)' do
              expect(page).to have_text 'username password'
            end
        
            # the second row
            within 'tr:nth-child(2)' do
              expect(page).to have_text 'bot1 bot1@mail.com'
            end
        
            # the third row
            within 'tr:nth-child(3)' do
              expect(page).to have_text 'bot2 bot2@mail.com'
            end
          end
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多