【问题标题】:How do i validate that a Record has been created in Rails?如何验证是否已在 Rails 中创建记录?
【发布时间】:2018-12-03 16:17:02
【问题描述】:

我有一个小型 Rails 应用程序,其数据库包含不同的 Languages。这些Languages 中的每一个都可能会发生变化,我想跟踪他们的变化。
我通过在对象更改时创建Audit 记录来做到这一点。这个Audit 有一个has_many :languages, through: :audit_language_couplings 字段来验证关系。

class Audit < ApplicationRecord
  belongs_to :audit_type

  has_many :audit_language_couplings
  has_many :languages, through: :audit_language_couplings

  validates_presence_of :audit_type, :admin_id, :date, :before
end

class Language < ApplicationRecord
  has_many :audit_language_couplings
  has_many :audits, through: :audit_language_couplings

  validates_presence_of :iso_code, :display_name, :keyboard_layout, :flag_url, :luis_app_identification, :luis_authorized_key, :luis_location
end

当调用PUTDELETEPOST 方法时,通过调用LanguagesController 中的create_audit() 方法创建审计。我还有一个/languages/:id/audits 端点,它以 JSON 格式返回给定语言的所有审计。

create_token() 方法:

def create_audit(type, admin_id)
    @language.audits.create(
      audit_type_id: type,
      admin_id: admin_id,
      date: Time.now.to_date,
      before: @language.to_s # TODO: Use the to-be-created to_json() or to_s() method instead.
    )
  end

这也是我的问题的本质(我认为)。 我目前正在使用 RSpec 和 Factory bot 请求测试我的 API。当我在测试中创建或更新Language 时,由于某种原因没有创建Audits。但我知道代码有效,因为当我在开发环境中使用邮递员手动执行此操作时它有效。

FactoryBot.define do
  factory :language do
    iso_code { Faker::Address.country_code }
    display_name { Faker::Address.country }
    keyboard_layout { Faker::Internet.url }
    flag_url { Faker::Internet.url }
    luis_app_identification { Faker::Lorem.characters(5) }
    luis_authorized_key { Faker::Lorem.characters(5) }
    luis_location { Faker::Lorem.characters(5) }
  end
end

我目前的测试结构如下:

describe 'POST /admin/language' do
    let(:valid_attributes) do
      {
        payload: {
          iso_code: 'en-US',
          display_name: 'English (US)',
          keyboard_layout: 'QWERTY',
          flag_url: '/public/images/en-US.png',
          luis_app_identification: 'test',
          luis_authorized_key: 'test',
          luis_location: 'test'
        }
      }
    end

    context 'when the request is valid' do
      before { post '/admin/languages', params: valid_attributes, headers: token_header }

      it 'creates a language' do
        expect(json['iso_code']).to eq('en-US')
      end

      it 'returns status code 201' do
        expect(response).to have_http_status(201)
      end

      context 'an audit should be made for the change' do
        before { get "/admin/languages/#{language_id}/audits", headers: token_header }

        it 'creates 1 audit' do
          expect(json.size).to eq 1
        end

        it 'is an audit of type 1 [ADD]' do
          expect(json[0]['audit_type_id']).to eq 1
        end
      end
    end

    context 'when the request is invalid' do
      before do
        post '/admin/languages', headers: token_header, params:
          {
            payload: {
              display_name: 'English (US)',
              keyboard_layout: 'QWERTY',
              flag_url: '/public/images/en-US.png',
              luis_app_identification: 'test',
              luis_authorized_key: 'test',
              luis_location: 'test'
            }
          }
      end

      it 'returns status code 422' do
        expect(response).to have_http_status(422)
      end

      it 'returns a validation failure message' do
        expect(response.body).to match(/Validation failed: Iso code can't be blank/)
      end
    end
  end

我检查审计的测试失败,因为使用 RSpec 运行代码时返回 0 个审计。

我认为我在工厂做错了什么,但我不确定,请告诉我!

干杯

【问题讨论】:

  • create_audit 方法在哪里调用?你能显示控制器代码吗?
  • 您可以做的一件事来解决问题是将您的 create_audit 方法更改为执行@language.audits.create!(。这样,如果创建失败,您将在 RSpec 中看到错误。
  • 为什么您认为这里需要审计和语言之间的多对多关联?我认为它实际上是一对多(审计belongs_to :language 和语言has_many :audits),因为您正在跟踪对单个记录的更改。
  • 好像你没有设置language_id
  • 谢谢 Pennypacker,我想我找到了问题所在,看来我的种子没有在启动时运行。因为它警告我在数据库中缺少 AuditType。我希望以前有人告诉过我这个把戏。我将尝试解决此问题并报告。最大,因为还有其他对象使用Audit,所以我的想法是审核不需要有LanguageAnswer 或任何其他记录。不确定我的火车在这里是否正确。

标签: ruby-on-rails ruby validation rails-activerecord


【解决方案1】:

很难根据提供的代码确切地说出发生了什么,但您可以尝试的一件事是将您的 create_audit 方法更改为:

def create_audit(type, admin_id)
    @language.audits.create!(

如果由于某种原因创建失败,将 ! (bang) 添加到 create 方法将引发异常,这应该会显示在您的 RSpec 日志中。这至少应该可以帮助您找到问题的根源。

【讨论】:

    【解决方案2】:

    您需要检查的是,当发布帖子时,Audit 实例的数量是否增加了 1。我会这样做:

    subject { post '/admin/languages', params: valid_attributes, headers: token_header }
    it 'creates an audit'
      expect { subject }.to change { Audit.count }.by(1)
    end
    

    此时您测试是否存在一个审计(不检查规范之前有多少)并通过间接方法(在第一个之后调用另一个 API 端点)。

    【讨论】:

      【解决方案3】:

      Pennycracker 和 ReggieB 的答案都是正确的,或者实际上是大局的一部分。

      关于create!() shebang 的部分将我引向了实际问题。工厂没有创建 AuditType 所依赖的 Audit
      ReggieB 建议我的测试设置有缺陷,因为我正在测试一个嵌套请求。

      我选择使用他建议的修改版本,更适合我当前的设置:

      context 'when the request is valid' do
        before { post '/admin/languages', params: valid_attributes, headers: token_header(admin_id: admin.id) }
      
        it 'returns status code 201' do
          expect(response).to have_http_status(201)
        end
      
        context 'the audit system has to be updated' do
          it 'creates 1 audit' do
            expect(Audit.all.size).to eq(1)
          end
      
          it 'should have type 1 [ADD]' do
            expect(Audit.first.audit_type.id).to eq(1)
          end
        end
      end
      

      数据库清理器 gem 会在每个示例之后清理数据库,因此检查第一个 Audit 的工作方式与预期更改相同。

      感谢大家的帮助。

      【讨论】:

        猜你喜欢
        • 2023-03-29
        • 2018-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多