【问题标题】:Rspec swallowing Postgres database exception on createRspec 在创建时吞下 Postgres 数据库异常
【发布时间】:2017-11-17 18:33:08
【问题描述】:

我无法理解 Rspec(rspec 3.7,rails 5.1.4)没有抛出与数据库约束相关的异常(如预期)的行为。

假设创建一个具有 not null 约束的表,如下所示:

create_table :ce_teams do |t|
  t.string :name
  t.integer :evaluation_id, null: false

  t.timestamps
end

对应型号是这样的:

module Ce
  class Team < ApplicationRecord
    belongs_to :evaluation
  end
end

然后在控制台内创建 Team 对象会引发 ActiveRecord::NotNullViolation 异常:

#\>RAILS_ENV=test rails c                                                                                                                                                                                                                                                              
Loading test environment (Rails 5.1.4)
2.4.2 :001 > t1 = Ce::Team.create(name: 'A-Team')                                                                                                                                                                                                                                                                             
   (0.2ms)  BEGIN
  SQL (1.2ms)  INSERT INTO "ce_teams" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "A-Team"], ["created_at", "2017-11-17 18:13:17.444797"], ["updated_at", "2017-11-17 18:13:17.444797"]]
   (0.2ms)  ROLLBACK
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  null value in column "evaluation_id" violates not-null constraint

在 rspec 中运行相同的操作不是

  it 'throwing not null exception if evaluation is missing in ctor' do
    expect {
      Ce::Team.create(name: 'UTEAM')
    }.to raise_exception(ActiveRecord::NotNullViolation)
  end

测试失败,一方面由于 createcreate! 的语义,这是预期的,但另一方面是意外的,因为不应该从模型验证中引发异常(在 create/创建!)但从保存记录的数据库中。

但是使用create! 会正确抛出异常,但它是一个不同的异常(我认为在堆栈中更高):

  1) Ce::Team Team Model throwing not null exception if evaluation is missing in ctor
     Failure/Error:
       expect {
         Ce::Team.create!(name: 'UTEAM')
       }.to raise_exception(ActiveRecord::NotNullViolation)

       expected ActiveRecord::NotNullViolation, got
         #<ActiveRecord::RecordInvalid: Validation failed: Evaluation must exist> 

发生了什么事?我的第一个直觉是 Rspec 中有一些层以不同的方式模拟创建(或保存),省略了实际的保存。

【问题讨论】:

    标签: ruby-on-rails rspec ruby-on-rails-5.1 ruby-on-rails-5


    【解决方案1】:

    我无法重现该问题。这是我得到的输出

    $ RAILS_ENV=development bin/rails c
    Running via Spring preloader in process 24202
    Loading development environment (Rails 5.1.4)
    2.4.0 :001 > team = Team.create(name: 'A-Team')
       (0.3ms)  BEGIN
       (0.3ms)  ROLLBACK
     => #<Team id: nil, name: "A-Team", created_at: nil, updated_at: nil, evaluations_id: nil> 
    2.4.0 :002 > team.errors.full_messages
     => ["Evaluation must exist"] 
    2.4.0 :003 > team = Team.create!(name: 'A-Team')
       (0.5ms)  BEGIN
       (0.5ms)  ROLLBACK
    ActiveRecord::RecordInvalid: Validation failed: Evaluation must exist
        from (irb):3
    

    如您所见,错误与createcreate! 相同。尝试使用bin/rails 而不是rails,我怀疑您使用的是2 个不同版本的Rails。

    【讨论】:

    • 感谢您尝试复制此内容。你在使用 postgres 吗?据我了解,空约束违规应该首先由 DB 抛出,然后由 rails 捕获。我确实在期望 RecordInvalid 的 rspec 中解决了这个问题,但它正在解决更广泛的不同错误类型。
    • 是的,我正在使用 PostgreSQL。在尝试将记录保存到数据库之前对其进行验证听起来更合乎逻辑。所以验证错误应该在数据库错误之前引发。 Available callbacks - Creating an object 证实了我的观点。
    • 我同意在实践中应该在任何数据库约束之前进行模型验证。然而,重点是通过 RSpec 测试 DB 约束。我的控制台输出是使用 RAILS_ENV=test 运行的,没有使用 spring,并且来自 Rails::Engine,而不是主应用程序。我将创建一个 Rails 示例应用程序并将其上传到 github 以在我有时间时重现此问题。
    • @count0 您没有在问题中明确说明要测试数据库约束。要测试数据库约束,您可以使用 save 而不是 create,因为它为您提供了绕过 Rails 验证的选项。
    猜你喜欢
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 2018-08-25
    相关资源
    最近更新 更多