rspec testando has_many: através de e depois de salvar
Eu tenho um (acho) relativamente simpleshas_many :through
relacionamento com uma tabela de junção:
class User < ActiveRecord::Base
has_many :user_following_thing_relationships
has_many :things, :through => :user_following_thing_relationships
end
class Thing < ActiveRecord::Base
has_many :user_following_thing_relationships
has_many :followers, :through => :user_following_thing_relationships, :source => :user
end
class UserFollowingThingRelationship < ActiveRecord::Base
belongs_to :thing
belongs_to :user
end
E esses testes rspec (sei que não são necessariamente bons testes, são apenas para ilustrar o que está acontecendo):
describe Thing do
before(:each) do
@user = User.create!(:name => "Fred")
@thing = Thing.create!(:name => "Foo")
@user.things << @thing
end
it "should have created a relationship" do
UserFollowingThingRelationship.first.user.should == @user
UserFollowingThingRelationship.first.thing.should == @thing
end
it "should have followers" do
@thing.followers.should == [@user]
end
end
Isso funciona bem até eu adicionar umafter_save
aoThing
modelo que referencia seufollowers
. Ou seja, se eu fizer
class Thing < ActiveRecord::Base
after_save :do_stuff
has_many :user_following_thing_relationships
has_many :followers, :through => :user_following_thing_relationships, :source => :user
def do_stuff
followers.each { |f| puts "I'm followed by #{f.name}" }
end
end
Em seguida, o segundo teste falha - ou seja, o relacionamento ainda é adicionado à tabela de junção, mas@thing.followers
retorna uma matriz vazia. Além disso, essa parte do retorno de chamada nunca é chamada (como sefollowers
está vazio no modelo). Se eu adicionar umputs "HI"
no retorno de chamada antes dofollowers.each
line, o "HI" aparece no stdout, então eu sei que o retorno de chamada está sendo chamado. Se eu comentar ofollowers.each
linha, depois os testes passam novament
Se eu fizer isso em todo o console, ele funciona bem. Ou seja, eu posso fazer
>> t = Thing.create!(:name => "Foo")
>> t.followers # []
>> u = User.create!(:name => "Bar")
>> u.things << t
>> t.followers # [u]
>> t.save # just to be super duper sure that the callback is triggered
>> t.followers # still [u]
Por que isso está falhando no rspec? Estou fazendo algo terrivelmente errado?
Atualiza
Tudo funciona se eu definir manualmenteThing#followers
Com
def followers
user_following_thing_relationships.all.map{ |r| r.user }
end
Isso me leva a acreditar que talvez eu esteja definindo meuhas_many :through
com:source
incorretamente?
Atualiza
Eu criei um projeto de exemplo mínimo e o coloquei no github:https: //github.com/dantswain/RspecHasMan
Outra Atualização
Obrigado a PeterNixey e @kikuchiyo pelas sugestões abaixo. A resposta final acabou sendo uma combinação das duas respostas e eu gostaria de poder dividir o crédito entre elas. Atualizei o projeto github com o que considero a solução mais limpa e realizei as alterações:https: //github.com/dantswain/RspecHasMan
Eu ainda adoraria se alguém me desse uma explicação realmente sólida sobre o que está acontecendo aqui. A parte mais preocupante para mim é por que, na declaração inicial do problema, tudo (exceto a operação do retorno de chamada) funcionaria se eu comentasse a referência afollowers
.