samedi 30 janvier 2016

Why doesn't my unit test reflect the changes to my records that PRY shows me exists?

I have a before :each in my specs that looks like this:

  before :each do
    @user1 = create(:user, gender: 0)
    @user2 = create(:user)
    @membership1 = create(:membership, member: nil, family_tree: @user1.family_tree, inviter: @user1, invited: @user2, relation: "sister", relative_type: 1)
    @membership2 = create(:membership, member: nil, family_tree: @user2.family_tree, inviter: @user2, invited: @user1, relation: "brother", relative_type: 1)
    @connection = create(:connection, inviter_membership: @membership1, invited_membership: @membership2, inviter_user: @user1, invited_user: @user2, request_status: 1)
    sign_in @user1
  end

And this is my unit test in question:

  it "should swap ownership of the inviter_membership - aka make invited_user the inviter_user on that inviter_membership" do
    delete :destroy, id: @user1
    expect(@membership1.inviter).to be @user2
  end

All my controller does is destroys the record passed in via the ID, like so:

  def destroy
    @user = User.find(params[:id])
    if @user.destroy
      redirect_to root_path, notice: "You have successfully cancelled your account."
    else
      redirect_to :back
    end
  end

However, in my User model, I have a before_destroy :callback and my associations like so:

class User < ActiveRecord::Base
  before_destroy :convert_user_to_members

  has_many :memberships
  has_many :inviter_memberships, class_name: "Membership", foreign_key: "user_id"
  has_many :invited_memberships, class_name: "Membership", foreign_key: "invited_id"
  has_many :inviter_connections, class_name: "Connection", foreign_key: "inviter_user_id"
  has_many :invited_connections, class_name: "Connection", foreign_key: "invited_user_id"

  private

    def convert_user_to_members
      inviter_mems = self.inviter_memberships
      if !inviter_mems.empty?
        inviter_mems.each do |membership|
          if membership.user?
            member = Member.create(first_name: self.first_name, last_name: self.last_name, email: self.email, bio: self.bio, gender: self.gender, avatar: self.avatar, birthday: self.birthday, deceased: false)
            invited_user = membership.invited
            membership.update!(member: member, inviter: invited_user, invited: nil, relative_type: 0)
            if !membership.inviter_connection.nil?
              connection = membership.inviter_connection
              connection.update!(inviter_membership: membership, invited_membership: nil, inviter_user: invited_user, invited_user: nil, request_status: 3)
            end
          end
        end
      end
    end

For the first binding.pry which is at the top of my test (i.e. before the destroy is called and the callback executed) this is what I get at my console:

   78:       it "should swap ownership of the inviter_membership - aka make invited_user the inviter_user on that inviter_membership" do
 => 79:         binding.pry
    80:         delete :destroy, id: @user1
    81:         binding.pry
    82:         expect(@membership1.inviter).to be @user2
    83:       end
    84:       #

[1] pry()> @user1.name
=> "Maybell McKenzie"
[2] pry()> @user2.name
=> "Joey Stanton"
[3] pry()> @membership1.inviter.name
=> "Maybell McKenzie"
[4] pry()> @membership1.invited.name
=> "Joey Stanton"
[5] pry()> @membership1.member
=> nil

For the second binding.pry, that gets executed within my callback, this is what happens:

    226:             invited_user = membership.invited
    227:             membership.update!(member: member, inviter: invited_user, invited: nil, relative_type: 0)
 => 228:             binding.pry
    229:             if !membership.inviter_connection.nil?
    231:               connection = membership.inviter_connection
    232:               connection.update!(inviter_membership: membership, invited_membership: nil, inviter_user: invited_user, invited_user: nil, request_status: 3)
    233:             end

[6] pry(#<User>)> self.name
=> "Maybell McKenzie"
[7] pry(#<User>)> invited_user.name
=> "Joey Stanton"
[8] pry(#<User>)> membership.inviter.name
=> "Joey Stanton"
[9] pry(#<User>)> membership.invited
=> nil
[10] pry(#<User>)> membership.member.name
=> "Maybell McKenzie"

Notice a few things:

  • the record currently being evaluated is a user record, that has the same name as @user1 in the first binding.pry - which is good.
  • the invited_user is correctly set to the same user record as @user2 above.
  • the membership.inviter.name is now the same as invited_user.name...aka...the swap has been successful.
  • the membership.invited attribute is now set to nil.
  • there is now a membership.member attribute and it is correctly set to the same user as @user1.

So, we are good so far. The next binding occurs AFTER the destroy has been completed. This is where things go awry. Remember there are no dependent: :destroy on any of the membership associations in my User model.

But this is the outcome of the third binding.pry:

    78:       it "should swap ownership of the inviter_membership - aka make invited_user the inviter_user on that inviter_membership" do
    79:         binding.pry
    80:         delete :destroy, id: @user1
 => 81:         binding.pry
    82:         expect(@membership1.inviter).to be @user2
    83:       end

[2] pry()> @user1.name
=> "Maybell McKenzie"
[3] pry()> @user2.name
=> "Joey Stanton"
[4] pry()> @membership1.inviter.name
=> "Maybell McKenzie"
[5] pry()> @membership1.invited.name
=> "Joey Stanton"
[6] pry()> @membership1.member
=> nil

Notice that everything looks identical to the first binding.pry.

If you dig deeper, you will notice that it seems that the record associated with @membership1 was actually destroyed....despite the fact that it shouldn't have been.

[7] pry()> Membership.count 
=> 1 # this should be 2, because no membership record should be deleted.
[8] pry()> Membership.all
=> [#<Membership id: 1986, family_tree_id: 34158, user_id: 14266, created_at: "2016-01-31 05:29:37", updated_at: "2016-01-31 05:29:37", relation: "brother", member_id: nil, invited_id: 14265, relative_type: 1>]
[9] pry()> @user1.id
=> 14265
[10] pry()> @user2.id
=> 14266

So why does the result of my test, not sync up with the results returned by my callback? Or how do I see the exact results sent from my callback to the test? There anyway for me to inspect that?

Aucun commentaire:

Enregistrer un commentaire