0

I have this table called Group.

class Group < ApplicationRecord
  belongs_to :admin, class_name: "User", foreign_key: :admin_id
  has_many :memberships, dependent: :destroy
  has_many :users, through: :memberships
  has_many :tasks, dependent: :destroy
  has_many :tags, dependent: :destroy
  has_many :invitations, dependent: :destroy
end

I am creating some tests for when I delete a group. After deleting a group I want to check that the group is deleted and that the associated records are deleted as well.

it "deletes the group" do
  expect(Group.find(group.id)).to_not be_present
end

it "deletes the group's dependent associations" do
  expect(Task.where(group: group).count).to eq 0
  expect(Tag.where(group: group).count).to eq 0
  expect(Invitation.where(group: group).count).to eq 0
  expect(Membership.where(group: group).count).to eq 0
end

I wrote it like that but it feels really repetitive. I was wondering if there is another way to create a query to get all the associated records in an array and check if the array is empty.

I tried with connected where like this:

Task.where(group: g).and(Tag.where(group: g)).and(Membership.where(group: g)).and(Invitation.where(group: g))

But it is throwing the following error: (Object doesn't support #inspect)

I read about joins but couldn't understand it properly so I am not sure if it is the right direction to go to.

Any help would be appreciated. Thank you!

PD: I am using PostgreSQL for the db

josegp
  • 499
  • 6
  • 21
  • Does this answer your question? [LEFT OUTER JOIN in Rails 4](https://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4) – anothermh Feb 26 '23 at 04:51
  • Rails has tests for `dependent: :destroy`, you don't need test it again. Test your code and don't test code of used libraries. It is some sort of antipattern – mechnicov Feb 26 '23 at 21:54

4 Answers4

2

I would not test this behavior because it only tests the implementation provided by Ruby on Rails and Rails methods are already tested in the framework internally.

When you really want to test your implementation, then I would suggest adding the shoulda-matcher gem and test that the associations are configured as expected, like this:

RSpec.describe Group do
  describe 'associations' do
    it { is_expected.to have_many(:invitations).dependent(:destroy) }
    it { is_expected.to have_many(:tags).dependent(:destroy) }
    it { is_expected.to have_many(:tasks).dependent(:destroy) }
    it { is_expected.to have_many(:users).dependent(:destroy) }
  end
end
Pascal
  • 8,464
  • 1
  • 20
  • 31
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • Thanks for the reply @spikckermann! Would you then test it on the model better. Instead of testing it on the controller (which is what I am doing now)? – josegp Feb 26 '23 at 07:38
  • 2
    Yes, I would test it in the model, because those associations are configured in the model. – spickermann Feb 26 '23 at 07:43
  • 2
    @spickermann I think the first sentence should be "I wouldn't test this behavior ..." – AbM Feb 26 '23 at 10:01
1

This is not a task for table joining since these tables have nothing to be joined on when the group is deleted.

You can remove the repetition by calling the expect in a loop:

it "deletes the group's dependent associations" do
  [Task, Tag, Invitation, Membership].each do |klass|
    expect(klass.where(group: group).count).to eq 0
  end
end
Jan Vítek
  • 671
  • 4
  • 11
1

To test behavior, I believe it's best to test one by one. But, in general, following good practices, instead of an it with several expectations, the best thing is an it for each expectation.

RSpec.describe Group do
  context 'when to destroy' do
    let(:group) { create(:group) }

    before { group.destroy }

    it { expect(group).to be_destroyed }

    describe 'associations' do
      it { expect(group.invitations.exists?).to be false }
      it { expect(group.tags.exists?).to be false }
      it { expect(group.tasks.exists?).to be false }
      it { expect(group.users.exists?).to be false }
    end
  end
end
1

You can use below to get all associations on record class

Group.reflect_on_all_associations

It will give you an array of associations. Sample association-

 #<ActiveRecord::Reflection::HasManyReflection:0x00007f7752b24578    
  @active_record=Group (call 'Group.connection' to establish a connection),
  @klass=nil,                                                        
  @name=:users,                                                      
  @options={},                                             
  @plural_name="users",                                    
  @scope=nil>

You can also use the reflect_on_association method to fetch details for a specific association

Group.reflect_on_association(:users)

Ajit
  • 11
  • 1