Ravn::SpecHelpers::WebHelpers::

HaveJSONBodyMatcher class

RSpec matcher for matching Rack::Test response body

Expect that the response consists of JSON of some sort:

expect( last_response ).to have_json_body

Expect that it’s a JSON body that deserializes as an Object:

expect( last_response ).to have_json_body( Object ) # -or- expect( last_response ).to have_json_body( Hash )

Expect that it’s a JSON body that deserializes as an Array:

expect( last_response ).to have_json_body( Array )

Expect that it’s a JSON body that deserializes as an Object that has expected keys:

expect( last_response ).to have_json_body( Object ). that_includes( :id, :first_name, :last_name )

Expect that it’s a JSON body that deserializes as an Object that has expected keys and values:

expect( last_response ).to have_json_body( Object ). that_includes( id: 118, first_name: ‘Princess’, last_name: ‘Buttercup’ )

Expect that it’s a JSON body that has other expected stuff:

expect( last_response ).to have_json_body( Object ). that_includes( last_name: a_string_matching(/humperdink/i), profile: a_hash_including(:age, :eyecolor, :tracking_ability) )

Expect a JSON Array with objects that all match the criteria:

expect( last_response ).to have_json_body( Array ). of_lenth( 20 ). and( all( be_an(Integer) ) )

Attributes

additional_expectations R
expected_type R
failure_description R
response R

Public Class Methods

new( expected_type=nil )

Create a new matcher that expects a response with a JSON body. If expected_type is not specified, any JSON body will be sufficient for a match.

# File lib/ravn/spec_helpers/web_helpers.rb, line 111
def initialize( expected_type=nil )
        @expected_type = expected_type
        @additional_expectations = []
        @response = nil
        @failure_description = nil
end

Public Instance Methods

and( *matchers )

Add the specified matchers as expectations of the Hash or Array that’s parsed from the JSON body.

# File lib/ravn/spec_helpers/web_helpers.rb, line 201
def and( *matchers )
        @additional_expectations.concat( matchers )
        return self
end
failure_message()

RSpec matcher API – return a message describing an expectation failure.

# File lib/ravn/spec_helpers/web_helpers.rb, line 138
def failure_message
        return "\n---\n%s\n---\n\nReason: %s\n" % [
                self.pretty_print_response,
                self.failure_description
        ]
end
failure_message_when_negated()

RSpec matcher API – return a message describing an expectation being met when the matcher was used in a negated context.

# File lib/ravn/spec_helpers/web_helpers.rb, line 148
def failure_message_when_negated
        msg = "expected response not to have a %s" % [ self.describe_type_expectation ]
        msg << " and " << self.describe_additional_expectations.join( ', ' ) unless
                self.additional_expectations.emtpy?
        msg << ", but it did."

        return "\n---\n%s\n---\n\nReason: %s\n" % [
                self.pretty_print_response,
                msg
        ]
end
matches?( response )

RSpec matcher API – returns true if all expectations of the specified response are met.

# File lib/ravn/spec_helpers/web_helpers.rb, line 127
def matches?( response )
        @response = response
        return self.correct_content_type? &&
                self.correct_json_type? &&
                self.matches_additional_expectations?
rescue Yajl::ParseError => err
        return self.fail_with "Response has invalid JSON body: %s" % [ err.message ]
end
of_length( number )

Add an additional expectation that the JSON body contain the specified number of members.

# File lib/ravn/spec_helpers/web_helpers.rb, line 192
def of_length( number )
        @additional_expectations << have_attributes( length: number )
        return self
end
Also aliased as: of_size
of_size( number )
Alias for: of_length
parsed_response_body()

Return the response’s body parsed as JSON.

# File lib/ravn/spec_helpers/web_helpers.rb, line 162
def parsed_response_body
        return @parsed_response_body ||= begin
                raw = Yajl::Parser.parse( self.response.body, check_utf8: true )
                Ravn::DataUtilities.symbolify_keys( raw )
        end
end
that_excludes( *memberset )

Add an additional expectation that the JSON body does not contain the specified members.

# File lib/ravn/spec_helpers/web_helpers.rb, line 184
def that_excludes( *memberset )
        @additional_expectations << exclude( *memberset )
        return self
end
that_includes( *memberset )

Add an additional expectation that the JSON body contains the specified members.

# File lib/ravn/spec_helpers/web_helpers.rb, line 175
def that_includes( *memberset )
        @additional_expectations << include( *memberset )
        return self
end
Also aliased as: which_includes
which_includes( *memberset )
Alias for: that_includes

Protected Instance Methods

correct_content_type?()

Returns true if the response has a JSON content-type header.

# File lib/ravn/spec_helpers/web_helpers.rb, line 250
def correct_content_type?
        content_type = self.response['content-type'] or
                return self.fail_with "response doesn't have a Content-type header"

        return fail_with "response's Content-type is %p" % [ content_type ] unless
                content_type.start_with?( 'application/json' )

        return true
end
correct_json_type?()

Check that the JSON body of the response has the correct type, if a type was specified.

# File lib/ravn/spec_helpers/web_helpers.rb, line 278
def correct_json_type?
        return self.parsed_response_body unless self.expected_type

        if self.expected_type == Array
                return self.fail_with( "response body isn't a JSON Array" ) unless
                        self.parsed_response_body.is_a?( Array )
        elsif self.expected_type == Object || self.expected_type == Hash
                return self.fail_with( "response body isn't a JSON Object" ) unless
                        self.parsed_response_body.is_a?( Hash )
        else
                warn "A valid JSON response can't be a %p!" % [ self.expected_type ]
        end

        return true
end
describe_additional_expectations()

Return an Array of descriptions of the members that were expected to be included in the response body, if any were specified. If none were specified, returns an empty Array.

# File lib/ravn/spec_helpers/web_helpers.rb, line 298
def describe_additional_expectations
        return self.additional_expectations.map( &:description )
end
describe_type_expectation()

Return an Array of text describing the expectation that the body be an Object or an Array, if a type was expected. If no type was expected, returns an empty Array.

# File lib/ravn/spec_helpers/web_helpers.rb, line 264
def describe_type_expectation
        return case self.expected_type
                when Object, Hash
                        "a JSON Object/Hash body"
                when Array
                        "a JSON Array body"
                else
                        "a JSON body"
                end
end
fail_with( message )

Return false after setting the failure message to message.

# File lib/ravn/spec_helpers/web_helpers.rb, line 242
def fail_with( message )
        @failure_description = message
        self.log.error "Failing with: %s" % [ message ]
        return false
end
matches_additional_expectations?()

Check that any additional matchers registered via the .and mutator also match the parsed response body.

# File lib/ravn/spec_helpers/web_helpers.rb, line 305
def matches_additional_expectations?
        return self.additional_expectations.all? do |matcher|
                matcher.matches?( self.parsed_response_body ) or
                        fail_with( matcher.failure_message )
        end
end
pretty_print_response()

Return a String that contains a pretty-printed version of the response object.

# File lib/ravn/spec_helpers/web_helpers.rb, line 212
def pretty_print_response
        return "%d %s HTTP/1.1\n%s\n\n%s" % [
                self.response.status,
                Rack::Utils::HTTP_STATUS_CODES[ self.response.status ],
                self.pretty_print_response_headers,
                self.pretty_print_response_body.encode('utf-8', invalid: :replace, undef: :replace)
        ]
end
pretty_print_response_body()

Return a String that contains a pretty-printed version of the response body.

# File lib/ravn/spec_helpers/web_helpers.rb, line 231
def pretty_print_response_body
        if @parsed_response_body
                return Yajl::Encoder.encode( @parsed_response_body, pretty: true, indent: "\t" )
        else
                data = self.response.body
                return data ? data[ 0, 1000 ] : '(empty body)'
        end
end
pretty_print_response_headers()

Return a String that contains a pretty-printed version of the response headers.

# File lib/ravn/spec_helpers/web_helpers.rb, line 223
def pretty_print_response_headers
        return self.response.headers.map do |name, val|
                "%s: %s" % [ name, val ]
        end.join( "\n" )
end