Home > Software design >  Rspec - test model that has two associations two the same column
Rspec - test model that has two associations two the same column

Time:10-07

In my app I created friend list for User. Inside Friend model I have validations for unique connection between users. Everything works good, but I don't know how to write tests for this model. It's looks like that:

Friend.rb

class Friend < ApplicationRecord
  belongs_to :user1, :class_name => 'User'
  belongs_to :user2, :class_name => 'User'

  validate :uniqueness_of_users_associations, :cant_be_friend_with_yourself

  def uniqueness_of_users_associations
    unless (user1.friends.where(user2: user2)   user1.is_friend.where(user1: user2)).blank?
      errors.add(:friend, 'He is already your friend')
    end
  end

  def cant_be_friend_with_yourself
    errors.add(:friend, "You can't be friend with yourself") if user1 == user2
  end
end

User.rb:

class User < ActiveRecord::Base
  extend Devise::Models 

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  include DeviseTokenAuth::Concerns::User

  has_many :friends, :class_name => 'Friend', :foreign_key => 'user1', dependent: :destroy
  has_many :is_friend, :class_name => 'Friend', :foreign_key => 'user2', dependent: :destroy
end

spec/factories/friends.rb :

FactoryBot.define do
  factory :friend do
    association :user1, factory: :user
    association :user2, factory: :user
    confirmed { true }
  end
end

friend_spec.rb :

RSpec.describe Friend, type: :model do
  describe 'relationships' do
    it { is_expected.to belong_to(:user1).class_name('User') }
    it { is_expected.to belong_to(:user2).class_name('User') }
  end
end

When I try to run test I get error:

Failure/Error: unless (user1.friends.where(user2: user2)   user1.is_friend.where(user1: user2)).blank?
     
     NoMethodError:
       undefined method `friends' for nil:NilClass

Why I get nil inside model? I did something wrong inside factory?

CodePudding user response:

What you actually want to find if Users 1 & 2 are friends is this query:

EXISTS (
 SELECT 1 
 FROM friends
 WHERE 
     friends.user1_id = 1 OR friends.user2_id = 1 
   AND 
     friends.user1_id = 2 OR friends.user2_id = 2
)
class Friend < ApplicationRecord

  validate :uniqueness_of_users_associations, :cant_be_friend_with_yourself

  def uniqueness_of_users_associations
    if Friend.between(user1.id, user2.id).exists?
      errors.add(:base, 'Friendship already exists')
    end
  end

  def self.between(a, b)
    user1_id, user2_id = arel_table[:user1_id], arel_table[:user2_id]
    where(user1_id.eq(a).or(user2_id.eq(a)))
    .where(user1_id.eq(b).or(user2_id.eq(b))) 
  end
  # ...
end

However the naming here is super off. The model should be named Friendship as friend actually means the person that you are friends with.

  • Related