pupeno/assert_difference

View on GitHub
lib/assert_difference/expectation.rb

Summary

Maintainability
A
0 mins
Test Coverage
module AssertDifference
  # This class represents a single expectations of the many that can be present in an assert_difference.
  #
  # This class is intended to be private to assert_difference and not used directly.
  #
  # @author José Pablo Fernández
  class Expectation
    DEFAULT_EXPECTATION = 1

    attr_accessor :expression, :expected_difference, :binding, :before_value, :after_value, :expected_value

    # Build an expectation.
    #
    # @param [String] expression The expression to be evaluated.
    # @param [Integer, Range] expected_difference The expected difference between running the expression before and
    #   after the body of the test.
    # @param [Binding] binding The context for the expressions to be evaluated in.
    def initialize(expression, expected_difference, binding)
      self.expression          = expression
      self.expected_difference = expected_difference
      self.binding             = binding

      self.before_value   = eval_expression
      self.expected_value = generate_expected_value
    end

    # Evaluate the expression and save the result as the after value, to be compared with the expected value.
    def eval_after
      self.after_value = eval_expression
    end

    # Returns whether the expectation passed or not.
    #
    # @return [Boolean] Whether the expectation passed or not.
    def passed?
      if expected_value.is_a?(Range)
        expected_value.include?(after_value)
      else
        expected_value == after_value
      end
    end

    # Generates the error message for this expectation. Returns nil if the expectation passed.
    #
    # @return [String, nil] The error message or nil if there's none.
    def error_message
      unless passed?
        "#{expression.inspect} didn't change by #{expected_difference} (expecting #{expected_value}, but got #{after_value})"
      end
    end

    # Build an array of expectations from a single, an array or a hash of raw expectations.
    #
    # @param [String, Array<String>, Hash<String, [Integer, Range]>] expectations Single expectation as a string, an array of expectations or hash table of
    #   expectations and expected difference.
    # @param [Integer, Range, nil] expected_difference Expected difference when using an array or single expression.
    # @param [Binding] binding The context in which the expressions are run.
    # @return [Array<Expectation>] Returns an array of {AssertDifference::Expectation} objects.
    def self.build_expectations(expectations, expected_difference, binding)
      if expectations.is_a? Hash
        raise Exception.new("When passing a hash of expressions/expectations, cannot define a global expectation.") unless expected_difference.nil?
        expectations.map do |expression, expected_difference|
          Expectation.new(expression, expected_difference, binding)
        end
      else
        Array.wrap(expectations).map do |expression|
          Expectation.new(expression, (expected_difference || DEFAULT_EXPECTATION), binding)
        end
      end
    end

    private

    # Generate the expected value.
    #
    # @return [Integer, Range] Generate the expected value.
    def generate_expected_value
      if expected_difference.is_a? Range
        (before_value + expected_difference.first)..(before_value + expected_difference.end)
      else
        before_value + expected_difference
      end
    end

    # Evaluate the expression in the context of the binding.
    #
    # @return [Object] Whatever the expression returns.
    def eval_expression
      eval(expression, binding)
    end

  end

  private_constant :Expectation
end