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.
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.callwas called).Desired solution
I'd like to write code like this
Alternatives considered
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.