Связанная запись равна нулю в интеграционном тесте после сохранения в контроллере - PullRequest
0 голосов
/ 17 марта 2020

В контроллере у меня есть метод 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_spe c .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, есть ли какой-нибудь традиционный способ тестирования связанной записи в тесте контроллера?

Ответы [ 2 ]

1 голос
/ 17 марта 2020

Не сохранял author перед тем, как связать его с book ...

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
0 голосов
/ 17 марта 2020

Прежде всего, я рекомендую вам сделать ваши контроллеры более общими, потому что это правильная архитектура, которой вы должны следовать, чтобы ваш контроллер мог называться 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

Это проверит общее количество книг, связанных с автором.

...