【问题标题】:Test that the 'save' function fails when invalid data is passed. (Ruby Rspec)测试传递无效数据时“保存”功能是否失败。 (红宝石 Rspec)
【发布时间】:2021-02-26 18:42:10
【问题描述】:

我正在开发一个在“项目”模型中具有以下验证标准的项目:

validates :project_name,
            presence: true,
            format: { with: /\A[a-zA-Z\s_-]+\z/,
                      message: 'allows letters, spaces, underscores, and hyphens' }
  validates :jira_id,
            uniqueness: true,
            presence: true,
            format: { with: /\A[A-Z]+-[0-9]+\z/,
                      message: 'allows capitalized letters followed by a hyphen and numbers' }
  validates :capacity,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }
  validates :openings,
            presence: true,
            numericality: { only_integer: true, greater_than_or_equal_to: 0 }

  validates_format_of :last_status_change_dt,
                      with: /\d{4}-\d{2}-\d{2}/,
                      message: 'must be formatted as YYYY-MM-DD',
                      on: :save

  validates_presence_of :last_status_change_dt

projects_controller:

def create
    ActiveRecord::Base.transaction do
      parameters = project_params.dup
      parameters[:language_id] = get_language_id(params[:language_name]) if parameters[:language_id].nil?
      @project = Project.new(parameters)
      if @project.save
        render json: @project, status: :created, location: @project
      else
        render json: @project.errors, status: :not_acceptable
        raise ActiveRecord::Rollback
      end
    end
  rescue ActiveRecord::ActiveRecordError
    render json: { text: 'Project was not created' }, status: :internal_server_error
  end

在 spec 文件中,使用了 FactoryBot build 和 create 函数。

在目前的测试中,验证依赖于项目模型,但我想测试保存功能。如何模拟保存功能失败?

【问题讨论】:

  • IMO 最好不要模拟保存功能......相反,只需在您的测试数据中引入验证错误(例如,给它一段无效数据)然后 save 将失败为结果
  • ActiveRecord::ActiveRecordError 是一个非常通用且不寻常的错误。与其在单个控制器中拯救它,不如在更高级别拯救它并发出更一般的错误可能更有意义。此外,如果 save 返回 false,您不必回滚。

标签: ruby-on-rails ruby rspec


【解决方案1】:

您可以使用allow_any_instance_of,但这有点像锤子。相反,请创建一个 double 并模拟 Project.new 以返回它。

context 'Project#save raises ActiveRecord::ActiveRecordError' do
  let(:project) {
    # Set up the double to fail on save.
    instance_double("Project").tap { |project|
      allow(project).to receive(:save).and_raise(ActiveRecord::ActiveRecordError)
    }
  }

  before {
    # Set up Project.new to return the double
    allow(Project).to receive(:new).and_return(project)
  }
end

请注意,save 引发 ActiveRecord::ActiveRecordError 是非常不寻常的,如果它做了一些非常不寻常的事情就出错了。您通常不会在控制器中拯救它。

【讨论】:

  • save 在实践中不会引发ActiveRecord::ActiveRecordError。它返回一个布尔值,指示记录是否已保存。你可能一直在想save!。这是存根实际上如何有害的一个示例。在这种情况下,它会强制测试做一些代码没有做的事情。
  • @maxpleaner 我最初的想法和你一样。 OP 似乎想要测试数据库何时着火。 IMO 我认为他们不应该在这个控制器中捕捉到它。无论哪种方式,它都是一种有用的模式。
  • 几周前我加入了这个项目,但并不了解 Ruby。我会把这个转达给我的主管,谢谢你指出这一点!
  • @Sugandha 更具体地说,这是 Ruby On Rails 框架(Ruby 是语言)。我建议您通过Rails Guides 熟悉一下。
  • 我知道你来自@Schwern。测试数据库何时着火是一件合理的事情。然而,它的处理方式通常不同——会有一个全局错误处理程序(通常由错误记录服务提供,例如 Bugsnag / New Relic / 等),客户端将收到 500,向用户显示一般错误消息。我听说向最终用户显示未处理错误的细节是一种糟糕的安全做法。
猜你喜欢
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
相关资源
最近更新 更多