Question

versions

rails: (4.0.0)
ruby: (2.0.0)
mongoid (4.0.0)
activemodel (~> 4.0.0)
moped (~> 2.0.beta5)

problem

I have an interface where I create a book by submitting an ISBN and a list of authors. My aim is to avoid duplicate book records, and to ensure that all the authors I've EVER added to a book with a specific ISBN get added to the original book, and that the new book does not actually get created.

I have a class that looks very similar to the book class below, and I've written some tests which I've also included below. I think the tests are correct for my purposes but please enlighten me if they're not testing what I think they are.

edit: These tests are not currently passing, so I must be doing something wrong somewhere. Are the tests written wrongly?

edit 2: I've commented in the specs to try to explain what the problems I'm facing are. There are a couple of failing tests and I don't understand what exactly is wrong.

edit 3: I've removed the extra specs, keeping only the specs that are failing

This is not the actual problem, but a generalised-ish version of it.

book class

class Book
  include Mongoid::Document
  include Mongoid::Timestamps

  before_create :add_authors_to_existing_book_if_present

  has_and_belongs_to_many :authors

  field :isbn, type: String

  validates_uniqueness_of :isbn

  def add_authors_to_existing_book_if_present
    if existing_book
      existing_book.authors << self.authors
      self.authors = []
      existing_book.save!

      # returns false to stop the model from
      # being created if existing_book is truthy
      false
    end
  end

  def existing_book
    Book.where(isbn: isbn).first
  end
end

author class

class Author
  include Mongoid::Document
  include Mongoid::Timestamps

  has_and_belongs_to_many :books

  field :name, type: String
end

model rspec tests

describe "#add_authors_to_existing_book_if_present" do
  context "when the book already exists in another author" do
    let(:first_author) { Author.new(name: "First", books: [first_book]) }
    let(:second_author) { Author.new(name: "Second", books: [second_book]) }
    let(:first_book) { Book.new(isbn: "12345") }
    let(:second_book) { Book.new(isbn: "12345") }

    before do
      first_author.save
      second_author.save
    end

    # fails
    it "only adds the first book to the second author" do
      expect(second_author.books).to eq [first_book]
      expect(second_author.books).not_to eq [second_book]
    end
    # this almost passes, but the author_ids in first_book is empty

    # fails
    it "assigns the second author to the first book" do
      expect(first_book.authors).to eq [first_author, second_author]
    end
    # only the first_author is part of the array
  end
end
Was it helpful?

Solution

In your test try doing first_book.reload.authors to make it pass, it just needs to reload to pick up the new authors after you run your model callbacks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top