Validacja i testy przy many-to-many

No i trafiłem na kolejny problem ale tu nie wiem jak się za to zabrać. Tabela books jest w relacji many-to-many z tabelą authors. Chcę ustawić validację tak by zawsze dana książka miała co najmniej jednego autora.
Migracja na podstawie pliku:

[code]class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books, :options => ‘DEFAULT CHARSET=utf8’ do |t|
t.string :title, :limit => 255, :null => false
t.integer :publisher_id, :null => false
t.datetime :published_at
t.string :isbn, :limit => 13, :unique => true
t.text :blurb
t.integer :page_count
t.float :price
t.timestamps
end

create_table :authors_books, :id => false do |t|
  t.integer :author_id, :null => false
  t.integer :book_id, :null => false
end

end

def self.down
drop_table :books
drop_table :authors_books
end
end[/code]
Model wygląda następująco:

[code]class Book < ActiveRecord::Base
belongs_to :publisher
has_and_belongs_to_many :authors

validates_length_of :title, :in => 2…255
validates_presence_of :publisher
validates_presence_of :published_at
validates_numericality_of :page_count, :only_integer => true
validates_numericality_of :price
validates_uniqueness_of :isbn
validates_format_of :isbn, :with => /[0-9-xX]{13}/
validates_presence_of :authors
end[/code]
Test przeprowadzam na podstawie pliku:

[code]require ‘test_helper’

class BookTest < ActiveSupport::TestCase
fixtures :authors, :publishers

def test_create
book = Book.new(
:title => ‘Ruby for Toddlers’,
:publisher_id => publishers(:one).id,
:authors => [authors(:one)],
:isbn => ‘123-123-123-1’,
:blurb => ‘The best book since “Bodo Bar”’,
:page_count => 12,
:price => 40.4
)
assert book.save
end
end[/code]
Wynik testu:

[code]test_create(BookTest)
[./test/unit/book_test.rb:18:in test_create' /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/testing/setup_and_teardown.rb:33:insend
/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/testing/setup_and_teardown.rb:33:in `run’]:
is not true.

1 tests, 1 assertions, 1 failures, 0 errors[/code]
Zastanawia mnie wpis w test.log:

Unable to load authors_book, underlying cause no such file to load -- authors_book

Tylko dlaczego authors_book a nie authors_books?

co do tego błędu to nie mam pojęcia, ale walidacje raczej musisz zrobić w metodzie validate

[code]class Book < ActiveRecord::Base
belongs_to :publisher
has_and_belongs_to_many :authors

validates_length_of :title, :in => 2…255
validates_presence_of :publisher
validates_presence_of :published_at
validates_numericality_of :page_count, :only_integer => true
validates_numericality_of :price
validates_uniqueness_of :isbn
validates_format_of :isbn, :with => /[0-9-xX]{13}/

def validate
if authors.size == 0
errors.add_to_base(‘Ksiązka musi mieć przynajmniej jednego autora’)
end
end
end[/code]

Pomijając sprawę testów poszedłem krok dalej i stworzyłem odpowiedni kontroler, widoki i zrobiłem ręczne testy. Akurat validacja wg. zapisu, który podałem działa doskonale. Sprawę validacji można zatem uznać za rozwiązaną.
Nadal jednak nie wiem w jaki sposób tworzyć testy. W widokach new oraz edit mam m.in taki wpis:

[code]<% form_for([:admin,@book]) do |f| %>
(…)

<%= f.label 'Authors' %>
<%= select_tag 'book[author_ids][]', options_from_collection_for_select(@authors, :id, :name, @book.authors.collect { |a| a.id }), { :multiple => true, :size => 5, :id => 'book_Authors' } %>

(...) <% end %>[/code] W efekcie [i]params[:book][/i] zawiera m.in. tabelę identyfikatorów autorów [i]author_ids[/i]. W momencie wykonania przypisania [i]@book=Book.new(params[:book])[/i] w obiekcie [i]@book[/i] tworzony jest hash [i]@attributes[/i] (zawierający wartości z formularza bezpośrednio pasujące do tabeli) oraz tabelę [i]@authors[/i] zawierającą obiekty Author odpowiednie do zawartości [i]author_ids[/i]. Niby wszystko jest OK ale jak to zrealizować w testach? Zapisy [i]:authors => [authors(:one)][/i] oraz [i]:author_ids => [authors(:one).id][/i] nie zwracają błędów ale rekord nie jest dodawany do bazy.

Dodaj w tym teście jakieś inspecty na tym obiekcie book to zobaczysz dlaczego się nie zapisuje do bazy

Co to są “inspecty”? Chodzi o polecenia assert… ?
Nie mniej podsunąłeś mi rozwiązanie. Okazało się, że problemu szukałem w niewłaściwym miejscu. W modelu mam podane m.in. validates_presence_of :published_at a w testach nie podawałem wartości dla tego pola.

Teraz pojawia się pytanie: jak testować, jakiego assert-a użyć, żeby nie tylko powiedział, że nie udało się wykonać book.save ale również pokazał dlaczego (jakiś komunikat błędu).

napisz w teście

puts book.errors.inspect

to ci wyświetli błędy walidacji