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.
Broken code
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 true
or 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 nil
.
How to validate the presence of a boolean column in Rails
According to Rails validations guides, we should use the inclusion
validator to ensure the required
boolean field is either true
or false
, but not nil
.
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!).
Validate your product
Partner with us and learn how we can validate your product together. Let’s talk!