Simple Spec That Validates_Associated

618 Views Asked by At

I am trying to test a validation on my Campaign model. I have a form that allows the user to Create a Campaign which should require that they choose a program before they can save it.

I tried following along a few answers here on SO but couldn't get them to work. Here's what I have so far..

The relationship..

class Campaign < ActiveRecord::Base
    belongs_to :program
    validates_associated :program, 
        message: "You need to choose a Program."
end

class Program < ActiveRecord::Base
    has_many :campaigns
end

..and the spec.

it 'validates associated campaign' do
  campaign = build(:campaign)
  expect(campaign.save).to be false
  expect(campaign.errors).to eq "You need to choose a Program."
end

The failure..

Failures:

  1) Campaign validates associated campaign
     Failure/Error: expect(campaign.save).to be false

       expected false
            got true
     # ./spec/models/campaign_spec.rb:34:in `block (2 levels) in <top (required)>'
2

There are 2 best solutions below

1
On BEST ANSWER

validates_associated only works when an associated object is present. In your example spec the campaign factory (I assume) does not add an associated program, thus no validation is performed, and the campaign is saved.

What you are looking for is validates :program, presence: true, for which valid? will return false if the program is missing.

See more info in the Rails Guide for ActiveRecord Validations.

0
On

Lacking a built-in matcher for validates_associated, I wrote this:

it { is_expected.not_to validate_presence_of(:program) }

context 'when program is present' do
  let(:program) { FactoryBot.create(:program) }
  before { subject.program = program }
  it { is_expected.to be_valid }

  context 'when program is present but not valid' do
    before { program.owner = nil }
    it { is_expected.not_to be_valid }
  end
end

This line: before { program.owner = nil } requires some knowledge of program's validations. That is not ideal from the context of Campaign's unit test, but isn't the worst programming sin I've ever made.