如何在 Rails 5 中使用 Capybara 和 Minitest 测试密码重置邮件程序

How to test a password reset mailer with Capybara and Minitest in Rails 5

我在 Rails 中使用许可 gem 以及 Capybara 和 Minitest,但我不知道如何测试密码重置邮件程序。我不是要测试 Clearance gem,它已经经过充分测试,但我想要一个高级集成测试以确保预期的用户体验不会中断,其中包括邮件程序。

这是我无法完成的测试 (/integration/password_reset_test.rb):

require 'test_helper'

class PasswordResetTest < ActionDispatch::IntegrationTest

  def setup
    ActionMailer::Base.deliveries.clear
    @user   = create(:user)
  end

  test "User can reset their password" do
    visit "/login"
    assert page.current_path == "/login"
    click_link "Forgot password?"
    assert page.current_path == "/passwords/new"
    assert_selector "h1", text: "Reset your password"
    fill_in("Email", :with => @user.email)
    click_button "Reset your password"
    assert page.current_path == "/passwords"
    assert_selector "h1", text: "Your password reset email is on the way."
    # This is where I'm stuck
    # Would like to test that the correct email was sent by checking
    #      basic content in the email and then simulate a user clicking
    #      the password reset link and then updating their password.
  end
end

您如何实际测试邮件是否已正确发送?有没有办法模拟水豚点击电子邮件中的密码重置 link,然后填写表格以重置密码?

我也试过了,但是这条线失败了,所以我显然做错了什么:

assert_equal 1, ActionMailer::Base.deliveries.size

我可以通过从服务器日志中获取密码重置电子邮件 link 来手动测试,因此该功能可以正常工作。

我可以在网上找到的所有示例都假设您使用的是 Rspec,但 Minitest 没有。我也尝试使用 capybara-email gem,但没有 Minitest 示例,我也无法使用它。

供参考: Gemfile test_helper.rb

为了自己想做的事capybara-email是不错的选择。要设置它,您可以将它包含在 ActionDispatch::IntegrationTest 中或包含在需要它的单独测试 class 中(在您当前的情况下 - PasswordResetTest)。您很可能还需要将 ActiveJob 配置为在作业排队时执行作业,而不是延迟它们(否则实际上不会发送电子邮件)。一种方法是包含 ActiveJob::TestHelper 然后使用它提供的 perform_enqueued_jobs 方法。这导致了类似

的事情
require 'test_helper'

class PasswordResetTest < ActionDispatch::IntegrationTest
  include Capybara::Email::DSL
  include ActiveJob::TestHelper

  def setup
    clear_emails
    @user   = create(:user)
  end

  test "User can reset their password" do
    perform_enqueued_jobs do
      visit "/login"
      assert_current_path("/login")
      click_link "Forgot password?"
      assert_current_path("/passwords/new")
      assert_selector "h1", text: "Reset your password"
      fill_in("Email", :with => @user.email)
      click_button "Reset your password"
      assert_current_path("/passwords")
      assert_selector "h1", text: "Your password reset email is on the way."
    end
    open_email(@user.email)
    assert_content(current_email, 'blah blah')
    current_email.click_link('Reset Password')
    assert_current_path(reset_password_path)
    ...  # fill out form with new password, etc.
  end
end

注意使用 assert_current_path 而不是 assert page.current_path... - 您通常更喜欢前者,因为后者没有 waiting/retrying 行为并且可能导致不稳定的测试。另请注意,如果您想更改路径名,那么编写在路径名中硬编码的测试会导致一场噩梦,因此您最好使用 rails 提供的路由助手来编写代码

assert_current_path(login_path)

等等