From 51c9f4e5e6876da8cec6066cac53c33cd90be3ee Mon Sep 17 00:00:00 2001 From: Horia Radu Date: Fri, 28 Apr 2017 11:40:33 +0300 Subject: [PATCH 1/3] Response body value validation Add the possibility to pass a block to the run_test! method in order to add expectations on your response --- Gemfile.lock | 1 + README.md | 10 +++++++++ .../lib/rswag/specs/example_group_helpers.rb | 6 ++--- .../lib/rswag/specs/example_helpers.rb | 4 ++-- .../lib/rswag/specs/response_validator.rb | 9 +++++--- rswag-specs/rswag-specs.gemspec | 3 ++- .../rswag/specs/response_validator_spec.rb | 22 +++++++++++++++++-- 7 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 60d9c8b..5afb841 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,7 @@ PATH remote: ./rswag-specs specs: rswag-specs (1.2.0) + json (~> 1.8) json-schema (~> 2.2) rails (>= 3.1, < 5.1) rspec-rails (>= 2.14, < 4) diff --git a/README.md b/README.md index f6db531..28e18b6 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,16 @@ If you've used [Swagger](http://swagger.io/specification) before, then the synta Take special note of the __run_test!__ method that's called within each response block. This tells rswag to create and execute a corresponding example. It builds and submits a request based on parameter descriptions and corresponding values that have been provided using the rspec "let" syntax. For example, the "post" description in the example above specifies a "body" parameter called "blog". It also lists 2 different responses. For the success case (i.e. the 201 response), notice how "let" is used to set the blog parameter to a value that matches the provided schema. For the failure case (i.e. the 422 response), notice how it's set to a value that does not match the provided schema. When the test is executed, rswag also validates the actual response code and, where applicable, the response body against the provided [JSON Schema](http://json-schema.org/documentation.html). +If you want to do additional validation on the JSON response, pass a block to the __run_test!__ method: + +```ruby +response '201', 'blog created' do + run_test! do |body| + expect(body['title']).to eq('foo') + end +end +``` + If you'd like your specs to be a little more explicit about what's going on here, you can replace the call to __run_test!__ with equivalent "before" and "it" blocks: ```ruby diff --git a/rswag-specs/lib/rswag/specs/example_group_helpers.rb b/rswag-specs/lib/rswag/specs/example_group_helpers.rb index cb96e71..ed7b20f 100644 --- a/rswag-specs/lib/rswag/specs/example_group_helpers.rb +++ b/rswag-specs/lib/rswag/specs/example_group_helpers.rb @@ -69,7 +69,7 @@ module Rswag metadata[:response][:examples] = example end - def run_test! + def run_test!(&block) # NOTE: rspec 2.x support if RSPEC_VERSION < 3 before do @@ -77,7 +77,7 @@ module Rswag end it "returns a #{metadata[:response][:code]} response" do - assert_response_matches_metadata(example.metadata) + assert_response_matches_metadata(example.metadata, &block) end else before do |example| @@ -85,7 +85,7 @@ module Rswag end it "returns a #{metadata[:response][:code]} response" do |example| - assert_response_matches_metadata(example.metadata) + assert_response_matches_metadata(example.metadata, &block) end end end diff --git a/rswag-specs/lib/rswag/specs/example_helpers.rb b/rswag-specs/lib/rswag/specs/example_helpers.rb index b9850ce..b7185fb 100644 --- a/rswag-specs/lib/rswag/specs/example_helpers.rb +++ b/rswag-specs/lib/rswag/specs/example_helpers.rb @@ -28,10 +28,10 @@ module Rswag end end - def assert_response_matches_metadata(api_metadata) + def assert_response_matches_metadata(api_metadata, &block) global_metadata = rswag_config.get_swagger_doc(api_metadata[:swagger_doc]) validator = ResponseValidator.new(api_metadata, global_metadata) - validator.validate!(response) + validator.validate!(response, &block) end private diff --git a/rswag-specs/lib/rswag/specs/response_validator.rb b/rswag-specs/lib/rswag/specs/response_validator.rb index 7212a3f..946c53d 100644 --- a/rswag-specs/lib/rswag/specs/response_validator.rb +++ b/rswag-specs/lib/rswag/specs/response_validator.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/slice' require 'json-schema' +require 'json' require 'rswag/specs/extended_schema' module Rswag @@ -11,10 +12,10 @@ module Rswag @global_metadata = global_metadata end - def validate!(response) + def validate!(response, &block) validate_code!(response.code) validate_headers!(response.headers) - validate_body!(response.body) + validate_body!(response.body, &block) end private @@ -34,7 +35,7 @@ module Rswag end end - def validate_body!(body) + def validate_body!(body, &block) response_schema = @api_metadata[:response][:schema] return if response_schema.nil? @@ -46,6 +47,8 @@ module Rswag rescue JSON::Schema::ValidationError => ex raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}" end + + block.call(JSON.parse(body)) if block_given? end end diff --git a/rswag-specs/rswag-specs.gemspec b/rswag-specs/rswag-specs.gemspec index 6bfd38b..6e866a6 100644 --- a/rswag-specs/rswag-specs.gemspec +++ b/rswag-specs/rswag-specs.gemspec @@ -16,7 +16,8 @@ Gem::Specification.new do |s| s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ] - s.add_dependency "rails", ">= 3.1", "< 5.1" + s.add_dependency "rails", ">= 3.1", "< 5.1" + s.add_dependency 'json', '~> 1.8' s.add_dependency 'json-schema', '~> 2.2' s.add_dependency 'rspec-rails', '>= 2.14', '< 4' end diff --git a/rswag-specs/spec/rswag/specs/response_validator_spec.rb b/rswag-specs/spec/rswag/specs/response_validator_spec.rb index 286940d..02316c7 100644 --- a/rswag-specs/spec/rswag/specs/response_validator_spec.rb +++ b/rswag-specs/spec/rswag/specs/response_validator_spec.rb @@ -29,7 +29,7 @@ module Rswag api_metadata[:response][:schema] = { type: 'object', properties: { text: { type: 'string' } }, - required: [ 'text' ] + required: ['text'] } end @@ -42,6 +42,24 @@ module Rswag let(:response) { OpenStruct.new(code: 200, body: "{\"foo\":\"Some comment\"}") } it { expect { call }.to raise_error UnexpectedResponse } end + + context "'block' provided" do + let(:call) do + subject.validate!(response) do |body| + expect(body['text']).to eq('Some comment') + end + end + + context 'the block validation passes' do + let(:response) { OpenStruct.new(code: 200, body: "{\"text\":\"Some comment\"}") } + it { expect { call }.to_not raise_error } + end + + context 'the block validation fails' do + let(:response) { OpenStruct.new(code: 200, body: "{\"text\":\"Some other comment\"}") } + it { expect { call }.to raise_error(RSpec::Expectations::ExpectationNotMetError) } + end + end end context "referenced 'schema' provided" do @@ -51,7 +69,7 @@ module Rswag author: { type: 'object', properties: { name: { type: 'string' } }, - required: [ 'name' ] + required: ['name'] } } end From 37f86f6d94bc1ef8fe85f7efd8a6777aa8d0e94c Mon Sep 17 00:00:00 2001 From: Horia Radu Date: Sat, 29 Apr 2017 12:06:57 +0300 Subject: [PATCH 2/3] yield entire response instead of only the body --- README.md | 7 ++++--- rswag-specs/lib/rswag/specs/response_validator.rb | 2 +- rswag-specs/spec/rswag/specs/response_validator_spec.rb | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 28e18b6..8d4355c 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,13 @@ If you've used [Swagger](http://swagger.io/specification) before, then the synta Take special note of the __run_test!__ method that's called within each response block. This tells rswag to create and execute a corresponding example. It builds and submits a request based on parameter descriptions and corresponding values that have been provided using the rspec "let" syntax. For example, the "post" description in the example above specifies a "body" parameter called "blog". It also lists 2 different responses. For the success case (i.e. the 201 response), notice how "let" is used to set the blog parameter to a value that matches the provided schema. For the failure case (i.e. the 422 response), notice how it's set to a value that does not match the provided schema. When the test is executed, rswag also validates the actual response code and, where applicable, the response body against the provided [JSON Schema](http://json-schema.org/documentation.html). -If you want to do additional validation on the JSON response, pass a block to the __run_test!__ method: +If you want to do additional validation on the response, pass a block to the __run_test!__ method: ```ruby response '201', 'blog created' do - run_test! do |body| - expect(body['title']).to eq('foo') + run_test! do |response| + data = JSON.parse(response) + expect(data['title']).to eq('foo') end end ``` diff --git a/rswag-specs/lib/rswag/specs/response_validator.rb b/rswag-specs/lib/rswag/specs/response_validator.rb index 946c53d..2e75097 100644 --- a/rswag-specs/lib/rswag/specs/response_validator.rb +++ b/rswag-specs/lib/rswag/specs/response_validator.rb @@ -48,7 +48,7 @@ module Rswag raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}" end - block.call(JSON.parse(body)) if block_given? + block.call(body) if block_given? end end diff --git a/rswag-specs/spec/rswag/specs/response_validator_spec.rb b/rswag-specs/spec/rswag/specs/response_validator_spec.rb index 02316c7..bf1bba6 100644 --- a/rswag-specs/spec/rswag/specs/response_validator_spec.rb +++ b/rswag-specs/spec/rswag/specs/response_validator_spec.rb @@ -46,7 +46,8 @@ module Rswag context "'block' provided" do let(:call) do subject.validate!(response) do |body| - expect(body['text']).to eq('Some comment') + data = JSON.parse(body) + expect(data['text']).to eq('Some comment') end end From f195c8275988e5061e01b21d2f38c5eae8455554 Mon Sep 17 00:00:00 2001 From: Horia Radu Date: Sat, 29 Apr 2017 21:17:53 +0300 Subject: [PATCH 3/3] yield entire response instead of only the body --- README.md | 2 +- rswag-specs/lib/rswag/specs/response_validator.rb | 5 ++--- rswag-specs/spec/rswag/specs/response_validator_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8d4355c..be69bbb 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ If you want to do additional validation on the response, pass a block to the __r ```ruby response '201', 'blog created' do run_test! do |response| - data = JSON.parse(response) + data = JSON.parse(response.body) expect(data['title']).to eq('foo') end end diff --git a/rswag-specs/lib/rswag/specs/response_validator.rb b/rswag-specs/lib/rswag/specs/response_validator.rb index 2e75097..7194953 100644 --- a/rswag-specs/lib/rswag/specs/response_validator.rb +++ b/rswag-specs/lib/rswag/specs/response_validator.rb @@ -16,6 +16,7 @@ module Rswag validate_code!(response.code) validate_headers!(response.headers) validate_body!(response.body, &block) + block.call(response) if block_given? end private @@ -35,7 +36,7 @@ module Rswag end end - def validate_body!(body, &block) + def validate_body!(body) response_schema = @api_metadata[:response][:schema] return if response_schema.nil? @@ -47,8 +48,6 @@ module Rswag rescue JSON::Schema::ValidationError => ex raise UnexpectedResponse, "Expected response body to match schema: #{ex.message}" end - - block.call(body) if block_given? end end diff --git a/rswag-specs/spec/rswag/specs/response_validator_spec.rb b/rswag-specs/spec/rswag/specs/response_validator_spec.rb index bf1bba6..cb95996 100644 --- a/rswag-specs/spec/rswag/specs/response_validator_spec.rb +++ b/rswag-specs/spec/rswag/specs/response_validator_spec.rb @@ -45,8 +45,8 @@ module Rswag context "'block' provided" do let(:call) do - subject.validate!(response) do |body| - data = JSON.parse(body) + subject.validate!(response) do |response| + data = JSON.parse(response.body) expect(data['text']).to eq('Some comment') end end