I believe all end-developer code (including constants) should be tested (as mentioned here). I'm not looking for a debate on that.
Assume I have these Ruby and/or Rails constants that I'd like to test using minitest (not RSpec). How would I go about that?
#####################################################################
#
# Constants
#
#####################################################################
THE_ANSWER_TO_EVERYTHING = 42
PI_WITH_PRECISION_FIVE = 3.14159
FIRST_TEN_PRIME_NUMBERS = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29].freeze
FAMOUS_CUTE_CATS = ['Grumpy Cat', 'Hello Kitty', 'Jinx the Cat'].freeze
HEXADECIMAL_COLOR_REGEX = /\A#\h{6}\z/.freeze
You can assume the above are constants on a model called CuteCat
, but the solution should work for any model, controller, PORO, etc.
I like to test Ruby constants in three ways:
Here is what that all looks like in minitest:
test/models/cute_cat_test.rb
# frozen_string_literal: true
require 'test_helper'
class CuteCatTest < ActiveSupport::TestCase
###################################################################
#
# Constants
#
###################################################################
test 'have a specific number of local, frozen constants' do
assert_constant_count(CuteCat, 5)
assert_constants_frozen(CuteCat)
end
###############################################
test 'describe the valid answer to everything' do
expected = 42
assert_equal(expected, CuteCat::THE_ANSWER_TO_EVERYTHING)
end
###############################################
test 'describe pi with a precision of five' do
expected = 3.14159
assert_equal(expected, CuteCat::PI_WITH_PRECISION_FIVE)
end
###############################################
test 'describe the first ten prime numbers' do
expected = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29].freeze
assert_equal(expected, CuteCat::FIRST_TEN_PRIME_NUMBERS)
end
###############################################
test 'describe the famous cute cats we care about' do
expected = ['Grumpy Cat', 'Hello Kitty', 'Jinx the Cat'].freeze
assert_equal(expected, CuteCat::FAMOUS_CUTE_CATS)
end
###############################################
test 'describe the hexadecimal color regular expression' do
expected = /\A#\h{6}\z/.freeze
assert_equal(expected, CuteCat::HEXADECIMAL_COLOR_REGEX)
end
end
You'll notice the canary test at the beginning is making use of two, custom methods. Here are the definitions for those methods:
test/test_helper.rb
#####################################################################
#
# Finders
#
#####################################################################
# Return constant names for the given class or module. It attempts to find names that are defined directly on the
# given class, are uppercase, and that do not refer to inner classes. This assumes that the given class follows the
# uppercase naming convention for its constants, while attempting to exclude names that refer to inner classes whose
# names are improperly uppercased, e.g., Facebook::API::URL.
def find_constants(klass)
inherited = false
constant_list = klass.constants(inherited)
constant_list.select { |c| c == c.upcase &&
klass.const_get(c).class != Class }
end
#####################################################################
#
# Assertions
#
#####################################################################
def assert_constant_count(klass, expected_count)
constant_list = find_constants(klass)
message = "Expected number of constants for #{klass.name} does not match actual count"
assert_equal(expected_count, constant_list.count, message)
end
def assert_constants_frozen(klass)
find_constants(klass).each do |constant|
message = "Expected constant #{klass.name}::#{constant} to be frozen"
assert(klass.const_get(constant).frozen?, message)
end
end