class RSpec::SleepingKingStudios::Matchers::ActiveModel::HaveErrorsMatcher

Matcher for testing ActiveModel object validations.

@since 1.0.0

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 15
def initialize
  super

  # The error and message expectations are set up through #on and
  # #with_message.
  @error_expectations = []
end

Public Instance Methods

description() click to toggle source

(see BaseMatcher#description)

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 24
def description
  message = 'have errors'

  attribute_messages = @error_expectations.select(&:expected).map do |expectation|
    attribute_message = ":#{expectation.attribute}"

    error_messages = expectation.messages.select(&:expected).map do |message_expectation|
      %{"#{message_expectation.message}"}
    end # map

    unless error_messages.empty?
      tools = ::SleepingKingStudios::Tools::ArrayTools

      attribute_message <<
        " with message#{error_messages.count == 1 ? '' : 's'} "\
        "#{tools.humanize_list error_messages}"
    end # unless

    attribute_message
  end # each
  message << " on #{attribute_messages.join(", and on ")}" unless attribute_messages.empty?

  message
end
does_not_match?(actual) click to toggle source

Checks if the object can be validated, whether the object is valid, and checks the errors on the object against the expected errors and messages from on and with_message, if any.

@param [Object] actual The object to test against the matcher.

@return [Boolean] True if the object responds to :valid? and is valid

or object.errors does not match the specified errors and messages (if
any); otherwise false.

@see matches?

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 60
def does_not_match? actual
  super

  @negative_expectation = true

  return false unless @validates = actual.respond_to?(:valid?)

  !matches?(actual)
end
failure_message() click to toggle source

(see BaseMatcher#failure_message)

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 143
def failure_message
  # Failure cases:
  # * object is not a model ("to respond to valid")
  # * expected one or more errors, but received none ("to have errors")
  # * expected one or more messages on :attribute, but received none or a
  #   subset ("to have errors on")

  if !@validates
    "expected #{@actual.inspect} to respond to :valid?"
  elsif expected_errors.empty?
    "expected #{@actual.inspect} to have errors"
  else
    "expected #{@actual.inspect} to have errors#{expected_errors_message}#{received_errors_message}"
  end # if-elsif-else
end
failure_message_when_negated() click to toggle source

(see BaseMatcher#failure_message_when_negated)

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 160
def failure_message_when_negated
  # Failure cases:
  # * object is not a model ("to respond to valid")
  # * expected one or more errors, received one or more ("not to have
  #   errors")
  # * expected one or more messages on attribute, received one or more
  #   ("not to have errors on")
  # * expected specific messages on attribute, received all ("not to have
  #   errors on")

  if !@validates
    "expected #{@actual.inspect} to respond to :valid?"
  elsif expected_errors.empty?
    return "expected #{@actual.inspect} not to have errors#{received_errors_message}"
  else
    return "expected #{@actual.inspect} not to have errors#{expected_errors_message}#{received_errors_message}"
  end # if-else
end
matches?(actual) click to toggle source

Checks if the object can be validated, whether the object is valid, and checks the errors on the object against the expected errors and messages from on and with_message, if any.

@param [Object] actual The object to test against the matcher.

@return [Boolean] True if the object responds to :valid?, is not valid,

and object.errors matches the specified errors and messages (if any);
otherwise false.

@see RSpec::SleepingKingStudios::Matchers::BaseMatcher#matches?

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 81
def matches? actual
  super

  return false unless @validates = actual.respond_to?(:valid?)

  !@actual.valid? && attributes_have_errors?
end
on(attribute) click to toggle source

Adds an error expectation. If the actual object does not have an error on the specified attribute, matches? will return false.

@param [String, Symbol] attribute

@return [HaveErrorsMatcher] self

@example Setting an error expectation

expect(actual).to have_errors.on(:foo)
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 98
def on attribute
  @error_expectations << ErrorExpectation.new(attribute)

  self
end
with(*messages)
Alias for: with_messages
with_message(message) click to toggle source

Adds a message expectation for the most recently added error attribute. If the actual object does not have an error on the that attribute with the specified message, matches? will return false.

@param [String, Regexp] message The expected error message. If a string,

matcher will check for an exact match; if a regular expression, matcher
will check if the message matches the regexp.

@raise [ArgumentError] If no error attribute has been added.

@return [HaveErrorsMatcher] self

@example Setting an error and a message expectation

expect(actual).to have_errors.on(:foo).with("can't be blank")

@see on

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 120
def with_message message
  raise ArgumentError.new "no attribute specified for error message" if
    @error_expectations.empty?

  @error_expectations.last.messages << MessageExpectation.new(message)

  self
end
with_messages(*messages) click to toggle source

Adds a set of message expectations for the most recently added error attribute.

@param [Array<String, Regexp>] messages

@see with_message

# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 135
def with_messages *messages
  messages.each do |message| self.with_message(message); end

  self
end
Also aliased as: with

Private Instance Methods

attributes_have_errors?() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 181
def attributes_have_errors?
  # Iterate through the received errors and match them against the expected
  # errors and messages.
  @actual.errors.messages.each do |attribute, messages|
    # Find the matching error expectation, if any.
    error_expectation = @error_expectations.detect do |error_expectation|
      error_expectation.attribute == attribute
    end # detect

    if error_expectation
      error_expectation.received = true

      # If the error includes message expectations, iterate through the
      # received messages.
      unless error_expectation.messages.empty?
        messages.each do |message|
          # Find the matching message expectation, if any.
          message_expectation = error_expectation.messages.detect do |message_expectation|
            if Regexp === message_expectation.message
              message =~ message_expectation.message
            else
              message == message_expectation.message
            end # if-else
          end # detect

          if message_expectation
            message_expectation.received = true
          else
            error_expectation.messages << MessageExpectation.new(message, false, true)
          end # if-else
        end # each
      end # unless
    else
      error_expectation = ErrorExpectation.new attribute, false, true
      messages.each do |message|
        error_expectation.messages << MessageExpectation.new(message, false, true)
      end # each

      @error_expectations << error_expectation
    end # if-else
  end # each

  missing_errors.empty? && missing_messages.empty?
end
expected_errors() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 226
def expected_errors
  @error_expectations.select do |error_expectation|
    error_expectation.expected
  end # select
end
expected_errors_message() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 250
def expected_errors_message
  "\n  expected errors:" + expected_errors.map do |error_expectation|
    "\n    #{error_expectation.attribute}: " + (error_expectation.messages.empty? ?
      "(#{@negative_expectation ? 'none' : 'any'})" :
      error_expectation.messages.expected.map(&:message).map(&:inspect).join(", "))
  end.join # map
end
missing_errors() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 232
def missing_errors
  @error_expectations.select do |error_expectation|
    error_expectation.expected && !error_expectation.received
  end # select
end
missing_messages() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 238
def missing_messages
  @error_expectations.select do |error_expectation|
    !error_expectation.messages.missing.empty?
  end # select
end
received_errors_message() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 258
def received_errors_message
  return "" unless @validates

  return "\n  received errors:\n    (none)" if @actual.errors.messages.empty?

  "\n  received errors:" + @actual.errors.messages.map do |attr, ary|
    "\n    #{attr}: " + ary.map(&:inspect).join(", ")
  end.join # map
end
unexpected_errors() click to toggle source
# File lib/rspec/sleeping_king_studios/matchers/active_model/have_errors_matcher.rb, line 244
def unexpected_errors
  @error_expectations.select do |error_expectation|
    !error_expectation.expected && error_expectation.received
  end # select
end