Search code examples
ruby-on-railsrubyunit-testingvalidationcustom-validators

Testing custom validation functions independently of models in rails app


I'm building a custom validation method for use in my rails app. The type of validator I want to build compares a column in the model where the validator is called to columns in other tables. The following is a code example that illustrates the pattern of the validator I am trying to build.

module ActiveModel
    module Validations
        module ClassMethods
            # example: validate_a_column_with_regard_to_other_tables :column, tables: { table_one: :some_column }
            def validate_a_column_with_regard_to_other_tables(*attr_names)
                validates_with WithRegardToOtherTablesValidator, _merge_attributes(attr_names)
            end
        end

        class WithRegardToOtherTablesValidator < EachValidator
            def validate_each(record, attribute, value)
                # compare record, attribute or value to all records that match table: :column in :tables
            end
        end
    end
end

It is possible to test this using the models that exist in the app and schema. However, this is not a good way to test the validator because it would describe the validator as being dependent on models that it is not dependent on.

The only other way I can think of is to create a set of mock up models in the test.

class ValidateModel < BaseModel
    validate_a_column_with_regard_to_other_tables :column, :tables { compare_to_model: :some_column }
end

class CompareToModel < BaseModel
    attr_accessor :some_column
end

However, it is not possible to validate that :column has something to do with :some_column in :compare_to_model because :compare_to_model is not part of the schema.

How do you create a mock model that is part of your schema in tests? Or is there a better way to test a custom validator function like this?


Solution

  • If you're using rspec you could set up something like this:

    before(:all) do
      ActiveRecord::Migration.create_table :compare_to_model do |t|
        t.string :some_column
        t.timestamps
      end
    end
    
    it "validates like it should" do
      ...
    end
    
    after(:all) do
      ActiveRecord::Migration.drop_table :compare_to_model
    end
    
    • One note on before(:all) it is a "global" setup so the data would persist from one it to the other, you might want to wrap each it with a transaction and roll it back after or instead have a before(:each) that will clean the table.