diff --git a/NEWS.md b/NEWS.md index 5d510cd..eb7da9b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,12 @@ master ====== +* Default `record_errors: true` [#52] +* Add support for configuring `record_errors` [#51] + +[#52]: https://github.com/thoughtbot/json_matchers/pull/52 +[#51]: https://github.com/thoughtbot/json_matchers/pull/51 + 0.6.3 ===== diff --git a/lib/json_matchers/configuration.rb b/lib/json_matchers/configuration.rb index 507919f..c54dcb0 100644 --- a/lib/json_matchers/configuration.rb +++ b/lib/json_matchers/configuration.rb @@ -11,7 +11,9 @@ class Configuration attr_reader :options def initialize - @options = {} + @options = { + record_errors: true, + } end end end diff --git a/lib/json_matchers/matcher.rb b/lib/json_matchers/matcher.rb index 1e5fc15..e91f0c3 100644 --- a/lib/json_matchers/matcher.rb +++ b/lib/json_matchers/matcher.rb @@ -1,5 +1,5 @@ require "json-schema" -require "json_matchers/payload" +require "json_matchers/validator" module JsonMatchers class Matcher @@ -9,47 +9,37 @@ def initialize(schema_path, options = {}) end def matches?(response) - # validate! will not raise and will always return true if you configure - # the validator to record errors, so we must instead inspect - # fully_validate's errors response - if options[:record_errors] - errors = JSON::Validator.fully_validate( - schema_path.to_s, - Payload.new(response).to_s, - options, - ) - - # errors is an array, but it will always only return a single item - if errors.any? - @validation_failure_message = errors.first - false - else - true - end - else - JSON::Validator.validate!( - schema_path.to_s, - Payload.new(response).to_s, - options, - ) - end - rescue JSON::Schema::ValidationError => ex - @validation_failure_message = ex.message + validator = build_validator(response) + + self.errors = validator.validate! + + errors.empty? + rescue JSON::Schema::ValidationError => error + self.errors = [error.message] false rescue JSON::Schema::JsonParseError raise InvalidSchemaError end def validation_failure_message - @validation_failure_message.to_s + errors.first.to_s end private attr_reader :schema_path, :options + attr_accessor :errors def default_options JsonMatchers.configuration.options || {} end + + def build_validator(response) + Validator.new( + options: options, + response: response, + schema_path: schema_path, + ) + end end end diff --git a/lib/json_matchers/validator.rb b/lib/json_matchers/validator.rb new file mode 100644 index 0000000..f4fd4cd --- /dev/null +++ b/lib/json_matchers/validator.rb @@ -0,0 +1,38 @@ +require "json-schema" +require "json_matchers/payload" + +module JsonMatchers + class Validator + def initialize(options:, response:, schema_path:) + @options = options.dup + @payload = Payload.new(response).to_s + @schema_path = schema_path.to_s + end + + def validate! + if recording_errors? + validate_recording_errors + else + validate + end + end + + private + + attr_reader :options, :payload, :schema_path + + def recording_errors? + options.fetch(:record_errors, false) + end + + def validate_recording_errors + JSON::Validator.fully_validate(schema_path, payload, options) + end + + def validate + JSON::Validator.validate!(schema_path, payload, options) + + [] + end + end +end diff --git a/spec/json_matchers/match_response_schema_spec.rb b/spec/json_matchers/match_response_schema_spec.rb index d8413ee..a634d83 100644 --- a/spec/json_matchers/match_response_schema_spec.rb +++ b/spec/json_matchers/match_response_schema_spec.rb @@ -159,13 +159,24 @@ end context "when configured to record errors" do - it "fails when the body is missing a required property" do + it "includes the reasons for failure in the exception's message" do with_options(record_errors: true) do - create_schema("foo_schema", - "type" => "object", - "required" => ["foo"]) - - expect(response_for({})).not_to match_response_schema("foo_schema") + create_schema("foo_schema", { + "type" => "object", + "properties" => { + "username" => { + "allOf": [ + { "type": "string" }, + { "minLength": 5 } + ] + } + } + }) + invalid_payload = response_for({ "username" => "foo" }) + + expect { + expect(invalid_payload).to match_response_schema("foo_schema") + }.to raise_error(/minimum/) end end end