Hi, I have the following situation. I have two models Item and Genre.
A Item has a name and a description. The genres-table contains a list
of possible genres wich can be associated to items. So a Item could be
associated with many genres and a genre could be associated with many
items. A typically n:m-relationship. I use the :has_many-:through
association, because i want do to a kind of on-delete-cascade in
Rails.
My models look as follows:
class Item < ActiveRecord::Base
n:m relationship between items and genres
has_many :items_genres, :dependent => :destroy
has_many :genres, :through => :items_genres
validates_presence_of :name
validate :must_have_at_least_one_associated_genre
protected
valdation to ensure, that a item has at least one associated genre
def must_have_at_least_one_associated_genre
# add an error message to the default error messages
errors.add(:genres, “must_be_choosen”) if
# if no genre is associated with this item
genres.size <= 0
end
end
I put two validations to the Item. The statement at line 6 make the
name arrtibute mandatory. In addition I would to ensure that a item
has at least one associated genre. So I decided to write a own
validation-method (line 10-17).
class ItemsGenre < ActiveRecord::Base
belongs_to :item
belongs_to :genre
end
class Genre < ActiveRecord::Base
n:m relationship between genres and items
has_many :items_genres, :dependent => :destroy
has_many :items, :through => :items_genres
end
I added some unit-tests to ensure that the validation works as
aspected. I wrote the following unit-test-methods.
require ‘test_helper’
class ItemTest < ActiveSupport::TestCase
def test_should_create_an_item
item = Item.new(
:name => “Item-Name”,
:description => “Item-Description”
)
# Assert, that the item is not valid and cannot be saved,
# because there are no genres associated with it
assert !item.valid?
assert !item.save
# Associate two genres with this item
item.genres = Genre.find_all_by_id([genres(:club).id, genres
(:commedy).id])
# Assert, that the item is now valid and can be saved
assert item.valid?
assert item.save
# Check if there are two genres associated with this item
assert_equal 2, item.genres.size
end
def test_should_require_name
# Create an item-object, but set name nil
item = Item.new(
:name => nil,
:description => “Item-Description”
)
# Assert, that the item is not valid and cannot be saved,
# because the name is nil
assert !item.valid?
assert !item.save
end
def test_should_require_description
# Create an item-object, but set name to nil
item = Item.new(
:name => “Item-Name”,
:description => nil
)
# Assert, that the item is not valid and cannot be saved,
# because the description is nil
assert !item.valid?
assert !item.save
end
end
The amazing thing is that all tree test-methods pass. But I only have
a validation for the name and the asscociated genres. I do not have a
validation of the description, but the test method for the description
passes.
I found out another thing. If I remove the validate-call in line 8 of
the Item-model, then all test-methods work as aspected.
In this case the test_should_create_an_item- and the
test_should_require_description-method both fail and the
test_should_require_name-method passes.
If I run the application and try to add a new item by the new-form,
all validation works as exspected. So it seems that only the unit-test
are faulty.
I am at a loss. I don´t know what I am doing wrong. Can anyone
reproduce this behavior? Or can anyone tell me how to write a
validation-method to ensure that at least one genre is associated with
an item. Maybe my test-method is wrong.
I hope someone can help me out.