Search code examples
ruby-on-railsrubyvalidationtesting

Ruby on Rails Test – Reset/Refresh Validators?


I am writing tests for a RoR Gem that has a validation that is dependent on a configuration variable:

class MyModel < ApplicationRecord
  validates_length_of :field_name, minimum: GemName.minimum_field_length
end

I can verify that this configuration option works as expected, however I want to ensure that it is covered in test:

class MyModelTest < ActiveSupport::TestCase
  test "field length is validated" do
    model = MyModel(field_name: 'a' * 25

    # Default minimum length is 20
    assert model.valid?

    # Test different length validation
    GemName.minimum_field_length = 30
    assert_not model.valid?
  end
end

The second assertion fails (the model is valid). If I add a debugger there and inspect, GemName.minimum_field_length gives me 30, indicating that it is changed, but if I look at the result of MyModel.validators, the minimum is still the original default value of 20. Presumably this is because the validators are created once, either when the test starts or when the model is called the first time (I have tried creating a new instance of MyModel after changing the config with no luck). Is there a way similar to .reset_column_information to refresh the model validators with the new configuration value during the test?


Solution

  • The problem is that defining a minimum length in a validation like this

    validates_length_of :field_name, minimum: GemName.minimum_field_length
    

    then it sets the minimum only once when the file is read for the first time. When GemName.minimum_field_length changes, then the configuration of the validator is not changed anymore.

    But when you initialize the validator with a lambda instead, then the lambda would be evaluated on every validator call and should therefore pick up changes.

    Try this instead:

    validates_length_of :field_name, minimum: -> { GemName.minimum_field_length }
    

    When you only want to ensure that the minimum length of the field equals the gem's configured minimum length, then you might want to write a test that checks if both values are equal, like this:

    minimum_field_name_length = MyModel.validators_on(:field_name)
                                       .select { |v| v.is_a?(LengthValidator) }
                                       .first
                                       .options[:minimum]
    
    assert_equal minimum_field_name_length, GemName.minimum_field_length