docs/errors/NonCaseSwappableValueError

@title NonCaseSwappableValueError

NonCaseSwappableValueError

This error is raised when using validate_uniqueness_of. This matcher, of
course, tests that an attribute disallows a non-unique value – and what
constitutes as “unique” depends on whether the case-sensitivity of that value
matters. If it does matter – meaning that the uniqueness validation in your
model isn’t using case_sensitive: false and you haven’t qualified the matcher
with case_insensitive – then the matcher will run the following test:

> Creating first a record with a value of “A”:
>
> * A new record with a value of “A” should not be valid (failing the uniqueness
> validation)
> * A new record with a value of “a” should be valid

The test value we’re using is in this case “A”, and this is what the matcher
will use if an existing record is not already present in the database. But if
a record already exists, then the matcher will use it as comparison – it will
read the attribute under test off of the record and use its value. So a better
example might be:

> Given an existing record with a value:
>
> * A new record with the same value should not be valid (failing the uniqueness
> validation)
> * A new record with the same value, but where the case is swapped (using
> String#swapcase), should be valid

Now, what happens if an existing record is there, but the value being used is
not one whose case can be swapped, such as "123" or "{-#%}"? Then the second
assertion cannot be made effectively.

So this is why you’re getting this exception. What can you do about it? As the
error message explains, you have two options:

  1. If you want the uniqueness validation in the model to operate
    case-sensitively and you didn’t mean to use a non-case-swappable value,
    then you need to provide an existing record with a different value, one that
    contains alpha characters. Here’s an example:

    # Model
    class User < ActiveRecord::Base
    validates_uniqueness_of :username
    end

    # RSpec
    RSpec.describe User, type: :model do
    context “validations” do
    subject do
    # Note that “123” == “123”.swapcase. This is a problem!
    User.new(username: “123”)
    end

    it do
    # So you can either override it like this, or just fix the subject.
    user = User.create!(username: “john123”)
    expect(user).to validate_uniqueness_of(:username)
    end
    end
    end

    # Minitest (Shoulda)
    class UserTest < ActiveSupport::TestCase
    context “validations” do
    subject do
    # Note that “123” == “123”.swapcase. This is a problem!
    User.new(username: “123”)
    end

    should “validate uniqueness of :username” do
    # So you can either override it like this, or just fix the subject.
    user = User.create!(username: “john123”)
    assert_accepts validate_uniqueness_of(:username), record
    end
    end
    end

  2. If you don’t want the uniqueness validation to operate case-sensitively,
    then you need to add case_sensitive: false to the validation and add
    case_insensitive to the matcher:

    # Model
    class User < ActiveRecord::Base
    validates_uniqueness_of :username, case_sensitive: false
    end

    # RSpec
    RSpec.describe User, type: :model do
    context “validations” do
    subject do
    # Note that “123” == “123”.swapcase, but it’s okay
    User.new(username: “123”)
    end

    it { should validate_uniqueness_of(:username).case_insensitive }
    end
    end

    # Minitest (Shoulda)
    class UserTest < ActiveSupport::TestCase
    context “validations” do
    subject do
    # Note that “123” == “123”.swapcase, but it’s okay
    User.new(username: “123”)
    end

    should validate_uniqueness_of(:username).case_insensitive
    end
    end