Question

Pretty sure these tests are working correctly. Got them to fail by removing the dependent: :destroy options on the has_many :relationships and has_many :reverse_relationships in user.rb.

Wanted to share what I did in case anyone else is working through Michael Hartl's Rails Tutorial 2nd Edition, Chapter 11 Exercises.

A few questions arose from this exercise (see bottom of this post). If anyone could help, that'd be great.

Chapter 11, Exercise 1:

Add tests for dependent :destroy in the Relationship model (Listing 11.4 and Listing 11.16) by following the example in Listing 10.15.

Here's my test: spec/models/user_spec.rb

require 'spec_helper'

describe User do

  before do
  @user = User.new(name: "Example User", email: "user@example.com", 
                   password: "foobar", password_confirmation: "foobar")
  end

  subject { @user }

  [...code omitted...]

  describe "relationship associations" do
    let(:other_user) { FactoryGirl.create(:user) }
    before do
      @user.save
      @user.follow!(other_user)
      other_user.follow!(@user)
    end

    it "should destroy associated relationships" do
      relationships = @user.relationships
      @user.destroy
      relationships.should be_empty
    end

    it "should destroy associated reverse relationships" do
      reverse_relationships = @user.reverse_relationships
      @user.destroy
      reverse_relationships.should be_empty
    end
  end

A couple questions arose from this exercise:

Question 1:

My initial tests were relationships.should be_nil reverse_relationships.should be_nil

But, realized an array was still being returned, despite no user existing. So, when a user doesn't exist and an association method is called, the result is still an array? Is this always true?

Question 2:

I wanted to play around with deleting relationships and reverse_relationships for a user in the rails console.

I tried this

> user = User.first
> user.relationships
 # returns a bunch of relationships
> user.relationships.destroy
=> []
> user.relationships
 # returns same bunch of relationships

How do I actually destroy the relationships permanently? Seems like good thing to know when exploring in console.

Thanks! I'm still pretty new to Rails

Was it helpful?

Solution

I'm a ruby/rails noob too.

Question 1: Searched rubyonrails.org for has_many and it says

Returns an array of all the associated objects. An empty array is returned if none are found.

On a side note, you can test for both nil and empty:

relationships.present?.should be_false

Question 2: The user.relationships.destroy requires an :id

user.relationships.destroy '1'

OTHER TIPS

may be you need smt like this

it { should have_many(:relationships).dependent(:destroy) }

Thanks for posting your code with your question. I only wanted to post this as a comment and not an answer, but it seems I can't yet. Anyway, I just wanted to add a small potential candidate to your tests, but from the other_user's perspective. The test is similar to the follow/unfollow tests, so hopefully it'd not too redundant, but it tests relationships directly and not the followed_users and followers that go through them.

describe "relationship associations" do
  ...
  context "when a follower/followed user is destroyed" do
    subject { other_user }

    before { user.destroy }

    its(:relationships) { should_not include(user) }
    its(:reverse_relationships) { should_not include(user) }
  end
end

Ruby on Rails Tutorial 2nd Edition.

Exercise 11.5.1 Add tests for destroying relationships associated with a given user.

This code works for me. I've tried to follow the example Listing 10.15.

spec/models/user_spec.rb

require 'spec_helper'

describe User do

  before do 
    @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar")
  end

  subject { @user }
  .
  .
  .
  .
  describe "user relationships associations" do
    let (:other_user) { FactoryGirl.create(:user) }
    let (:another_user) { FactoryGirl.create(:user) }

    before do
      @user.save
      @user.follow!(other_user)
      @user.follow!(another_user)
      other_user.follow!(@user)
      other_user.follow!(another_user)
      another_user.follow!(@user)
      another_user.follow!(other_user)
    end

    its(:followed_users) { should include(other_user) }
    its(:followers) { should include(another_user) }

    it "should destroy associated followers" do
      followers = @user.followers
      @user.destroy
      followers.each do |follower|
        follower.followed_users.should_not include(@user)
      end
    end

    it "should destroy associated followed users" do
      followed_users = @user.followed_users
      @user.destroy
      followed_users.each do |followed_user|
        followed_user.followers.should_not include(@user)
      end
    end
  end
end

Re: paul, the relationships array is not constituted by users, so his include() should always be false, so the test always green. Re: maria, it appears that the followed_users and followers methods won't return a user who doesn't exist, even when if a relationship referencing he or she remains. So this test is never red also.

another solution:

  describe "relationships" do
    let(:other_user) { FactoryGirl.create(:user) }
    before do
      @user.save
      @user.follow!(other_user)
    end

    let(:relationship) { @user.relationships.last }

    describe "should be destroyed when the followed user is destroyed" do
      before { other_user.destroy }
      its(:relationships) { should_not include(relationship) }
    end

    describe "should be destroyed when the following user is destroyed" do
      subject { other_user }
      before { @user.destroy }
      its(:reverse_relationships) { should_not include(relationship) }
    end
  end

The above answers work, but I figure I would share mine it's shorter.. :D

describe "following" do

  let(:other_user) { FactoryGirl.create(:user) }
  before do
    @user.save
    @user.follow!(other_user)
    other_user.follow!(@user)
  end

   it { should be_following(other_user) }
   its(:followed_users) { should include(other_user) }

   it "should destroy associated followed_users and followers" do
     @user.destroy
     @user.relationships.present?.should be_false
     @user.reverse_relationships.present?.should be_false

     expect(other_user.followers).not_to include(@user)
     expect(other_user.followed_users).not_to include(@user)
   end
   .
   .
   .
   .
 end
end

P.S you can leave out:

@user.relationships.present?.should be_false
@user.reverse_relationships.present?.should be_false

but I throw it in there for someone who wants to make sure that all associated destroy action is at work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top