存入controller后集成测试关联记录为nil

Associated record is nil in integration test after saving in controller

在控制器中,我有一个 update 方法,它创建一个记录(称为 book),将其关联到现有记录(称为 author)并保存它。

Book属于一个Author

add_author_to_book_controller.rb

def update
  @author = App::Models::Author.new(params)
  @book = App::Models::Book.where(id: params[:book_id]).first
  @book.author = @author
  @book.save!
  # this works fine...
  # puts @book.author.inspect 
  render json: { status: :ok }
end

add_author_to_book_controller_spec.rb

describe App::AddAuthorToBookController do
  describe '#update' do
    # this is a contrived example, there is more setup regarding creating the "book" properly...
    let(:name) { 'foobar' }
    let(:action) { xhr :put, :update, params }
    let(:params) { { first_name: name } }
    subject { book }
    before { action }

    it { expect(response.status).to eq 200 }
    it 'should save the author to the book' do
      # why is author nil here?
      # puts book.author.inspect
      expect(book.author.first_name).to eq name
    end
  end
end

我在测试中尝试了 book.reload,但没有成功。我是 rails 的新手,是否有一些传统的方法可以在控制器测试中测试关联记录?

首先我建议你让你的控制器更通用,因为这是你需要遵循的正确架构,所以你的控制器可以被称为 authors_controller.rb 并管理所有作者的东西或 books_controller.rb并管理所有书籍的东西。按照这种方法,您可以有一个方法 associate_book 接收作者和一本书并创建正确的关联。让我用代码解释一下:

class Author < ApplicationRecord
  has_many :books
  # Fields :name
  validates :name, presence: true
end

class Book < AoplicationRecord
  # Optional because I think you want to add the author after create it
  belongs_to :author, optional: true
  # Fields :title, :publish_year, :author_id
  validates :title, :publish_year, :author_id, presence: true
end

class AuthorsController < ApplicationController
  def associate_book
    # params here will contain the following [:author_id, book_id]
    author = Author.find(params[:author_id])
    book = Book.find(params[:book_id])
    book.author = author
    book.save!
  rescue ActiveRecord::RecordInvalid => error
    # This will allow you to catch exceptions related to the update
  end
end

然后您可以通过执行以下操作来测试此方法,假设此方法将从路由中调用

# Testing with RSpec
# spec/controllers/authors_controller.rb
RSpec.describe AuthorsController do
  let(:author) { Author.first }
  let(:book) { Book.first }
  it 'Should associate an author with a provided book' do
    expect do
      post :associate_book, params: { author_id: author.id, book_id: book.id }
    end.to change { author.books.count }.by(1)
  end
end

这将检查与作者关联的图书总数。

在将其关联到 book 之前未保存 author...

def update
  @author = App::Models::Author.new(params)
  # was simply missing this
  @author.save!
  @book = App::Models::Book.where(id: params[:book_id]).first
  @book.author = @author
  @book.save!
  # this works fine...
  # puts @book.author.inspect 
  render json: { status: :ok }
end