Hi, I’m testing the dependent: :destroy functionality for a many-to-many association in a TDD way. So to check that the associated records are deleted in the join table (theme_projects) I wrote this integration test:
scenario "Dependent associations are destroyed when admin deletes research project" do
research_theme_1 = Fabricate(:research_theme)
research_theme_2 = Fabricate(:research_theme)
@research_project.research_themes << research_theme_1
@research_project.research_themes << research_theme_2
themes = @research_project.research_themes
click_link "Delete"
themes.each do |theme|
theme.research_projects.should_not include(@research_project)
end
end
I expected this test to fail, becauce I hadn’t yet added dependent: :destroy to my many to many association:
class ResearchProject < ActiveRecord::Base
validates :title, :body, presence: true
has_many :theme_projects
has_many :research_themes, through: :theme_projects
end
To my surprise however the test passes. When I do a sanity check, the records for the deleted research project are not removed from the join tabel.
So is my test not valid?
When I add the dependent: :destroy directive to my many to many association, the records for a deleted research project are removed from the join table.
There’s stuff going on here that I cant see because you apparently have it tucked away in a before block (stting the instance variable, for instance). So as a sidebar to this discussion, I’d suggest checking out: Let's Not
Anyway, I think what is happening is that the way your accessing and adding to the research_themes is not also updating the in-memory copy of your research project. I don’t know what Fabricate is, but I’m guessing it’s similar to FactoryGirl and is createing a ResearchTheme. You likely need some inverse_of declarations on your associations. See: ActiveRecord::Associations::ClassMethods
Hi Derek, thanks for your reply. Yes some stuff is tucked away in a before block:
background do
@research_project = Fabricate(:research_project)
admin = Fabricate(:admin)
sign_in(admin)
visit admin_research_projects_path
end
I agree with Joe Ferris that you shouldn’t tuck away to much so that you’re test aren’t readable anymore, but if it’s the same setup code you repeat in every every test, than having your setup code defined just once in a before block is really handy.
The problem I’m having with this test is not that it passes, but also when I remove the ‘dependent: destroy’ methods from my research_theme model and research_project model, the test passes. So as a mather of fact, I can’t make the test fail.
Did you look at the inverse_of documentation I linked to? It appears as though your creating related data via one relationship and asserting against that data from what is supposed to be an equivalent relationship. It’s possible Rails hasn’t drawn the connection that one relationship is the inverse of the other.
Not sure where to apply ‘inverse of’. It’s a many-to-many association. Research Projects have many Research Themes and Research Themes have many Research Projects
my join model ThemeProject looks like this:
class ThemeProject < ActiveRecord::Base
belongs_to :research_theme
belongs_to :research_project
end
So I should apply ‘inverse of’ like so? :
class ThemeProject < ActiveRecord::Base
belongs_to :research_theme, inverse_of: :research_project
belongs_to :research_project, inverse_of: :research_theme
end