There is a subtle bug that happens when validating the presence of boolean fields in Rails. Ensuring adequate test coverage helps us find those issues before they hit production.
Here is an example that looks right but hides a subtle problem:
class Question < ApplicationRecord validates :required, presence: true end
Here is the spec that describes the behavior we want. We want
required to be either
false but not
nil. This helps avoid the Three-state boolean problem.
describe "#required" do it "only allows true or false" do expect(Question.new(required: false)).to be_valid expect(Question.new(required: true)).to be_valid expect(Question.new(required: nil)).to be_invalid end end
But when we run the test, we get an error message:
Failure/Error: expect(Question.new(required: false)).to be_valid expected #<Question id: nil, required: false, created_at: nil, updated_at: nil> to be valid, but got errors: Required can't be blank
Aha! The error message highlights the subtle bug in our implementation. The spec fails because the Rails
presence validator rejects any falsy values, not just
According to Rails validations guides, we should use the
inclusion validator to ensure the
required boolean field is either
false, but not
class Question < ApplicationRecord validates :required, inclusion: [true, false] end
Now, running the spec again, it passes and we avoided getting paged for an incident during the night 🎉
(Thanks, Joël Quenneville, for the great review!).