1

I'm new to rails, I have a trip class with three foreign key. Two of these associate it with the same class: Place.

This is my model:

class Trip < ApplicationRecord
    belongs_to :from, class_name: "Place", foreign_key: "from_id"
    belongs_to :to, class_name: "Place", foreign_key: "to_id"
    belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"

    validates :price, presence: true
    validates :time, presence: true
    validates :from_id, presence: true
    validates :to_id, presence: true, if: :from_different_to?
    
    def from_different_to?
        to_id != from_id
    end
end

All model tests pass except for the last one:

class TripTest < ActiveSupport::TestCase

  def setup
    @place1 = Place.create(name:"NewYork",cap:"11111",lat:"1234",long:"1478")
    @place2 = Place.create(name:"Los Angeles", cap:"22222", lat:"1234",long:"1478")
    @vehicle = Vehicle.create(targa: "ab123cd",modello:"500",marca:"Fiat", posti:5,alimentazione:"benzina")
    @trip = Trip.new(price: 10, time: Time.new(2021, 10, 14, 12,03), from_id: @place1.id, to_id: @place2.id,vehicle_id: @vehicle.id)  
  end
...

test "Departure id and arrival id should be different" do
    @trip.to_id = @place1.id
    assert_not @trip.valid?
  end

that result in a failure:

Failure:
TripTest#test_Departure_id_and_arrival_id_should_be_different [/media/alessandro/DATA/Universita/Magistrale/1_anno/Programmazione_concorrente/hitchhiker/test/models/trip_test.rb:45]:
Expected true to be nil or false

I'm not able to understand why. Can someone help me?

2
  • @spickermann the record IS valid, but they expect it not to be. I'm not sure why. Commented Oct 19, 2021 at 19:18
  • @spickermann in setup I set to_id: @place2.id, but in the test I set @trip.to_id = place1.id, so to_id == from_id == @place1.id. This because I want an invalid trip to try if effectively the model reject this record Commented Oct 20, 2021 at 8:32

3 Answers 3

1

It seems like you think validates ... if: works differently as it actually does. This line

validates :to_id, presence: true, if: :from_different_to?

translates to validate that the to_id is present if the from_different_to method returns true. When from_different_to evaluates to false then do not validate. See Rails Guides.

That means when you define

@trip.to_id = @place1.id
assert_not @trip.valid?

in your test then the first line disables the check for the presence of the to_id. No validation, no error...

I suppose what you really try to achieve is to validate that to to_id is present and from_id and to_id are not equal. This can be done with a custom validation like this:

validates :to_id, presence: true
validate :validates_places_are_different

private
def validates_places_are_different
  errors.add(:to_id, "must be different to from_id") if to_id == from_id
end
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, you've hit the problem. I misunderstood how if: works. Thank you for the answer, I solved my problem, unfortunately I don't have the needed reputation to give you a +1, but your help was invaluable.
0

I'm not able to understand why. Can someone help me?

That if conditionally enables a validation. Your to_id is the same as from_id and so to_id is not validated at all. But even if it was, to_id has a value, so there wouldn't be an error from this field.


Overall, I'm not quite sure why are you expecting a validation error here or what that error should be. In my experience, assertions like assert_not @model.valid? are virtually useless. The record might not be valid because of unrelated reasons and you'll have no idea. Personally, I assert the exact error message I'm expecting. Something along these lines (rspec syntax)

it "requires first_name" do
  expected_messages = {
    first_name: [:blank],
  }

  @model.valid?
  expect(@model.errors.full_messages).to eq expected_messages
end

2 Comments

The concept is: from_id and to_id are equal, so if @trip.valid? return false, means that my model (Trip) has rejected the record (how it should do), assert_not false => the test is passed. The problem is that my model doesn't reject the record, so my question is why this happens?
@AleCrout Yes, I understand how assert_not @trip.valid? works. My question was, I don't understand why you expected the model to be valid. But it looks like spickermann gave you a better answer.
0

An alternative to that of @spickermann is that:

class Trip < ApplicationRecord
    belongs_to :from, class_name: "Place", foreign_key: "from_id"
    belongs_to :to, class_name: "Place", foreign_key: "to_id"
    belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"

    validates :price, presence: true
    validates :time, presence: true
    validates :from_id, presence: true
    validates :to_id, numericality: {other_than: :from_id}, if: :from_place_id?

    def from_place_id
        from_id
    end

    def from_place_id?
        !from_id.nil?
    end
end

Note that we have to put a control to execute the last validates only if from_id is not null, because if we doesn't do that, we vanificate the control validates :from_id, presence:true on the superior line.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.