Skip to content

Normalize-Matcher option to assert normalization callable is defined for "normalize with:" #1704

@felix-as

Description

@felix-as

Problem this feature will solve

With Rails 7.2+ normalization feature, attributes can be normalized with anything callable.
Shoulda-matcher implements behavioral testing ( it { is_expected.to normalize(:name).from("FREDI").to("Fredi") }).

However, (as with validators) there might be the need to extract the normalization code into e.g. a NameNormalizer (which then can be unit tested).
Currently, shoulda matcher does not provide a method to spec that this (extracted) normalizer will be used.

Instead, one can resort to mocks (and assert that NameNormalizer.call was called).

Desired solution

I'd like to write code like this

# Fantasy code
# will not work, but sketches the idea
    context 'when subject uses a constant callable' do
      it 'matches' do
        NameNormalizer = lambda do |name_attr_value|
          return name_attr_value.to_s.strip.downcase
        end
        model = define_model(:User, email: :string) do
          normalizes :name, with: NameNormalizer
        end

        expect(model.new).to normalize(:name).with(NameNormalizer)
      end
    end

Alternatives considered

  • "Only" test behavior with shared examples.
  • Mock the Normalizer, assign a value and assert it was called.

Update I refered to validator specs earlier, but realized that we use some custom code to check for validator definitions, which are not part of shoulda-matchers.
Update2 Copilot claims to have an implementation for this. I won't pretend that I looked deep enough into it to validate its soundness (also, i am not familiar with either rspec nor shoulda-matchers code base). It however gave insights into how to assert these without syntactic sugar (class.type_for_attribute(:attr).normalizer, which is only defined if it actually is of NormalizedValueType, which is not obvious because of DelegateClass stuff) . PR is linked, feel free to use it; Sorry for not having more capacities.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions