我如何验证记录已在 Rails 中创建?

How do i validate that a Record has been created in Rails?

我有一个小型 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 端点 returns 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 机器人请求测试我的 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 方法更改为:

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

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

你需要检查的是,当post发生时,Audit实例的数量增加了一个。我会这样做:

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

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

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

关于 create!() shebang 的部分将我引向实际问题。工厂没有创建 Audit 所依赖的 AuditType
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 与预期更改的工作方式相同。

感谢大家的帮助。