diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c0cb26..436f67d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ No stricter / less strict request validation. Mostly internal stuff. Plus a Sina - Removed: `OpenapiFirst::Test::Coverage::TerminalReporter#format`, deprecated since 3.4.0. Use `#report` instead. #### Added +- API coverage now reports skipped requests and responses. - Added: OpenAPI 3.2 documents are accepted, but not fully supported yet. They are handled using the OpenAPI 3.1 rules, so features introduced in 3.2 may be ignored. Loading such a document prints a warning. Operations defined under `additionalOperations` are routed. See #469. - Added: `OpenapiFirst::Request#parameters` returns the parameters that are defined for a request as `OpenapiFirst::Parameter` objects, which expose `name`, `location`, `schema`, `required?`, `deprecated?`, `style`, `explode?` and `media_type`. It used to return an internal object with a different interface. - Added: Show all covered endpoints in HTML coverage reporter and filter covered/uncovered endpoints diff --git a/Gemfile.lock b/Gemfile.lock index cd265ab5..539afe40 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -273,6 +273,7 @@ GEM zeitwerk (2.8.2) PLATFORMS + arm64-darwin-23 arm64-darwin-24 arm64-darwin-25 x86_64-linux diff --git a/lib/openapi_first/test/coverage.rb b/lib/openapi_first/test/coverage.rb index a7a62f72..40ccfe33 100644 --- a/lib/openapi_first/test/coverage.rb +++ b/lib/openapi_first/test/coverage.rb @@ -4,6 +4,7 @@ require_relative 'coverage/tracker' require_relative 'coverage/covered_request' require_relative 'coverage/covered_response' +require_relative 'coverage/skipped_summary' require 'drb' module OpenapiFirst @@ -15,7 +16,15 @@ module Coverage autoload :TerminalReporter, 'openapi_first/test/coverage/terminal_reporter' autoload :HtmlReporter, 'openapi_first/test/coverage/html_reporter' - Result = Data.define(:plans, :coverage) + Result = Data.define(:plans, :coverage) do + def skipped_requests_count + plans.sum(&:skipped_requests_count) + end + + def skipped_responses_count + plans.sum(&:skipped_responses_count) + end + end class << self def start(skip_response: nil, skip_route: nil) diff --git a/lib/openapi_first/test/coverage/html_reporter.css b/lib/openapi_first/test/coverage/html_reporter.css index f9e598f6..dd861653 100644 --- a/lib/openapi_first/test/coverage/html_reporter.css +++ b/lib/openapi_first/test/coverage/html_reporter.css @@ -28,6 +28,7 @@ --color-error: #f87171; --color-info: #7dd3fc; --color-warning: #fbbf24; + --color-skipped: #fb923c; } body { @@ -112,17 +113,36 @@ body:has(#filter-covered:not(:checked)) details.route.is-covered { body:has(#filter-uncovered:not(:checked)) details.route.is-uncovered { display: none; } +body:has(#filter-skipped-requests:not(:checked)) details.route.is-skipped { + display: none; +} /* Hide a plan once none of its routes remain visible. */ body:has(#filter-covered:not(:checked)) - details.plan:not(:has(details.route.is-uncovered)) { - display: none; -} + details.plan:not(:has(details.route.is-uncovered)):not( + :has(details.route.is-skipped) + ), body:has(#filter-uncovered:not(:checked)) - details.plan:not(:has(details.route.is-covered)) { - display: none; -} + details.plan:not(:has(details.route.is-covered)):not( + :has(details.route.is-skipped) + ), +body:has(#filter-skipped-requests:not(:checked)) + details.plan:not(:has(details.route.is-covered)):not( + :has(details.route.is-uncovered) + ), body:has(#filter-covered:not(:checked)):has(#filter-uncovered:not(:checked)) + details.plan:not(:has(details.route.is-skipped)), +body:has(#filter-covered:not(:checked)):has( + #filter-skipped-requests:not(:checked) + ) + details.plan:not(:has(details.route.is-uncovered)), +body:has(#filter-uncovered:not(:checked)):has( + #filter-skipped-requests:not(:checked) + ) + details.plan:not(:has(details.route.is-covered)), +body:has(#filter-covered:not(:checked)):has( + #filter-uncovered:not(:checked) + ):has(#filter-skipped-requests:not(:checked)) details.plan { display: none; } @@ -261,6 +281,21 @@ li { color: var(--color-success); } +.skipped { + color: var(--color-skipped); +} + +.skipped-note, +.skipped-summary { + color: var(--color-skipped); + font-size: var(--font-size-xs); +} + +.skipped-summary { + margin-inline-start: 0.75rem; + white-space: nowrap; +} + .status { color: var(--text-primary); min-width: 3ch; diff --git a/lib/openapi_first/test/coverage/html_reporter.html.erb b/lib/openapi_first/test/coverage/html_reporter.html.erb index 674e6acc..2cb51943 100644 --- a/lib/openapi_first/test/coverage/html_reporter.html.erb +++ b/lib/openapi_first/test/coverage/html_reporter.html.erb @@ -10,9 +10,13 @@

API Coverage: <%= coverage.round(1) %>%

Generated at: <%= h generated_at %> + <% if any_skipped? -%> + <%= skipped_requests_count %> request(s) and <%= skipped_responses_count %> response(s) skipped + <% end -%>
+
@@ -29,18 +33,23 @@ <% visible_routes(plan).each do |route| -%> <% requests = request_items(route) -%> <% responses = response_items(route) -%> -
> +
> <%= h route.request_method.upcase %> <%= h route.path %> <% status = route_status(route) -%> - <% if status == :request_problem -%> - <%= h explain_unfinished_request(route.requests.first) %> + <% if status == :skipped -%> + Skipped + <% elsif status == :request_problem -%> + <%= h explain_unfinished_request(route.tracked_requests.first) %> <% elsif status == :responses_problem -%> ⚠️ <%= uncovered_responses_count(route) %> response(s) not covered <% else -%> <% end -%> + <% if route.skipped_responses.any? && status != :skipped -%> + <%= route.skipped_responses.count %> response(s) skipped + <% end -%> <% if (label = route.summary) -%> <%= h label %> <% end -%> @@ -49,7 +58,12 @@ <% unless requests.empty? -%>
    <% requests.each do |request| -%> - <% if request.finished? -%> + <% if request.skipped? -%> +
  • + <% if request.content_type -%><%= h request.content_type %><% end -%> + Skipped, not covered by any test +
  • + <% elsif request.finished? -%>
  • <% if request.content_type -%><%= h request.content_type %><% end -%> @@ -67,7 +81,13 @@ <% unless responses.empty? -%>
      <% responses.each do |response| -%> - <% if response.finished? -%> + <% if response.skipped? -%> +
    • + <%= h response.status %> + <% if response.content_type -%><%= h response.content_type %><% end -%> + Skipped +
    • + <% elsif response.finished? -%>
    • <%= h response.status %> diff --git a/lib/openapi_first/test/coverage/html_reporter.rb b/lib/openapi_first/test/coverage/html_reporter.rb index 56134a13..e18012ff 100644 --- a/lib/openapi_first/test/coverage/html_reporter.rb +++ b/lib/openapi_first/test/coverage/html_reporter.rb @@ -8,6 +8,8 @@ module Test module Coverage # Writes a self-contained HTML coverage report to a file. class HtmlReporter + include SkippedSummary + def initialize(output: 'coverage/openapi_coverage.html', verbose: false, logger: Test.logger) @output = output @verbose = verbose @@ -21,11 +23,14 @@ def report(coverage_result) coverage_result.plans.each do |plan| next if plan.coverage >= 100 - @logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage.round(4)}%" + logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage.round(4)}%" end - @logger.info "API coverage report written to #{@output}" + log_skipped_summary(coverage_result) + logger.info "API coverage report written to #{@output}" end + private attr_reader :logger + TEMPLATE_PATH = File.join(__dir__, 'html_reporter.html.erb') TEMPLATE = ERB.new(File.read(TEMPLATE_PATH), trim_mode: '-') TEMPLATE.filename = TEMPLATE_PATH diff --git a/lib/openapi_first/test/coverage/html_reporter/context.rb b/lib/openapi_first/test/coverage/html_reporter/context.rb index d2009dab..280153e9 100644 --- a/lib/openapi_first/test/coverage/html_reporter/context.rb +++ b/lib/openapi_first/test/coverage/html_reporter/context.rb @@ -12,11 +12,14 @@ class Context 'API Coverage did not detect any API requests for the registered ' \ 'API descriptions. Make sure to observe your application using OpenapiFirst::Test.' - attr_reader :coverage, :plans, :verbose, :generated_at + attr_reader :coverage, :plans, :verbose, :generated_at, + :skipped_requests_count, :skipped_responses_count def initialize(coverage_result, verbose, generated_at: Time.now) @coverage = coverage_result.coverage @plans = coverage_result.plans + @skipped_requests_count = coverage_result.skipped_requests_count + @skipped_responses_count = coverage_result.skipped_responses_count @verbose = verbose @generated_at = generated_at.strftime('%Y-%m-%d %H:%M:%S %z') end @@ -27,6 +30,10 @@ def get_binding # rubocop:disable Naming/AccessorMethodName binding end + def any_skipped? + skipped_requests_count.positive? || skipped_responses_count.positive? + end + def visible_routes(plan) plan.routes end @@ -36,17 +43,25 @@ def any_request_made?(route) end def route_status(route) - return :request_problem if route.requests.none?(&:finished?) - return :responses_problem if any_request_made?(route) && route.responses.any? { |r| !r.finished? } + return :skipped if route.skipped? + return :request_problem if route.tracked_requests.none?(&:finished?) + return :responses_problem if any_request_made?(route) && route.tracked_responses.any? { |r| !r.finished? } :ok end + def route_class(route) + return 'is-skipped' if route.skipped? + + route.finished? ? 'is-covered' : 'is-uncovered' + end + def uncovered_responses_count(route) - route.responses.count { |r| !r.finished? } + route.tracked_responses.count { |r| !r.finished? } end def request_items(route) + return route.requests if route.skipped? return [] unless any_request_made?(route) && route.requests.any?(&:content_type) route.requests diff --git a/lib/openapi_first/test/coverage/plan.rb b/lib/openapi_first/test/coverage/plan.rb index 092f8690..e2b17098 100644 --- a/lib/openapi_first/test/coverage/plan.rb +++ b/lib/openapi_first/test/coverage/plan.rb @@ -14,14 +14,13 @@ class UnknownRequestError < StandardError; end def self.for(oad, skip_response: nil, skip_route: nil) plan = new(definition_key: oad.key, filepath: oad.filepath, title: oad.title) - routes = oad.routes - routes = routes.reject { |route| skip_route[route.path, route.request_method] } if skip_route - routes.each do |route| - responses = skip_response ? route.responses.reject(&skip_response) : route.responses + oad.routes.each do |route| plan.add_route request_method: route.request_method, path: route.path, requests: route.requests, - responses: + responses: route.responses, + skipped: skip_route ? skip_route[route.path, route.request_method] : false, + skip_response: end plan end @@ -63,15 +62,36 @@ def tasks index.values end - def add_route(request_method:, path:, requests:, responses:) - request_tasks = requests.to_a.map do |request| - index[request.key] = RequestTask.new(request) - end + def skipped_requests + routes.flat_map(&:skipped_requests) + end + + def skipped_responses + routes.flat_map(&:skipped_responses) + end + + def skipped_requests_count + skipped_requests.count + end + + def skipped_responses_count + skipped_responses.count + end + + def add_route(request_method:, path:, requests:, responses:, skipped: false, skip_response: nil) + request_tasks = requests.to_a.map { |request| add_task RequestTask.new(request, skipped:) } response_tasks = responses.to_a.map do |response| - index[response.key] = ResponseTask.new(response) + add_task ResponseTask.new(response, skipped: skipped || (skip_response ? skip_response[response] : false)) end @routes << RouteTask.new(path:, request_method:, requests: request_tasks, responses: response_tasks) end + + private + + def add_task(task) + index[task.key] = task unless task.skipped? + task + end end end end diff --git a/lib/openapi_first/test/coverage/request_task.rb b/lib/openapi_first/test/coverage/request_task.rb index 4d9362ec..600531b0 100644 --- a/lib/openapi_first/test/coverage/request_task.rb +++ b/lib/openapi_first/test/coverage/request_task.rb @@ -9,10 +9,11 @@ module Coverage class RequestTask extend Forwardable - def_delegators :@request, :path, :request_method, :content_type + def_delegators :@request, :key, :path, :request_method, :content_type - def initialize(request_definition) + def initialize(request_definition, skipped: false) @request = request_definition + @skipped = skipped @requested = false @last_error_message = nil end @@ -25,6 +26,10 @@ def track(validated_request) @last_error_message = validated_request.error.exception_message unless validated_request.valid? end + def skipped? + @skipped == true + end + def requested? @requested == true end diff --git a/lib/openapi_first/test/coverage/response_task.rb b/lib/openapi_first/test/coverage/response_task.rb index 5b48116b..f637604b 100644 --- a/lib/openapi_first/test/coverage/response_task.rb +++ b/lib/openapi_first/test/coverage/response_task.rb @@ -11,8 +11,9 @@ class ResponseTask def_delegators :@response, :status, :content_type, :key - def initialize(response_definition) + def initialize(response_definition, skipped: false) @response = response_definition + @skipped = skipped @responded = false @last_error_message = nil end @@ -25,6 +26,10 @@ def track(validated_response) @last_error_message = validated_response.error.exception_message unless validated_response.valid? end + def skipped? + @skipped == true + end + def responded? @responded == true end diff --git a/lib/openapi_first/test/coverage/route_task.rb b/lib/openapi_first/test/coverage/route_task.rb index 7250243c..ff6275ff 100644 --- a/lib/openapi_first/test/coverage/route_task.rb +++ b/lib/openapi_first/test/coverage/route_task.rb @@ -4,8 +4,30 @@ module OpenapiFirst module Test module Coverage RouteTask = Data.define(:path, :request_method, :requests, :responses) do + def skipped? + requests.all?(&:skipped?) + end + + def tracked_requests + requests.reject(&:skipped?) + end + + def tracked_responses + responses.reject(&:skipped?) + end + + def skipped_requests + requests.select(&:skipped?) + end + + def skipped_responses + responses.select(&:skipped?) + end + def finished? - requests.all?(&:finished?) && responses.all?(&:finished?) + return false if skipped? + + tracked_requests.all?(&:finished?) && tracked_responses.all?(&:finished?) end def summary diff --git a/lib/openapi_first/test/coverage/skipped_summary.rb b/lib/openapi_first/test/coverage/skipped_summary.rb new file mode 100644 index 00000000..ee02e3f7 --- /dev/null +++ b/lib/openapi_first/test/coverage/skipped_summary.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module OpenapiFirst + module Test + module Coverage + # Shared reporting of skipped requests and responses for coverage reporters. + module SkippedSummary + def log_skipped_summary(coverage_result) + requests = coverage_result.skipped_requests_count + responses = coverage_result.skipped_responses_count + return if requests.zero? && responses.zero? + + logger.info "API coverage skipped #{requests} request(s) and #{responses} response(s)." + return if requests.zero? + + logger.warn "#{requests} request(s) were skipped entirely. " \ + 'These requests are excluded from API coverage and are not contract-tested at all.' + end + end + end + end +end diff --git a/lib/openapi_first/test/coverage/terminal_reporter.rb b/lib/openapi_first/test/coverage/terminal_reporter.rb index c7491cc0..abbc7a87 100644 --- a/lib/openapi_first/test/coverage/terminal_reporter.rb +++ b/lib/openapi_first/test/coverage/terminal_reporter.rb @@ -5,6 +5,8 @@ module Test module Coverage # Reports coverage to a logger using ANSI-coloured lines. class TerminalReporter + include SkippedSummary + def initialize(verbose: false, focused: true, logger: Test.logger) @verbose = verbose @focused = focused && !verbose @@ -18,13 +20,14 @@ def report(coverage_result) 'Make sure to observe your application using OpenapiFirst::Test.' end coverage_result.plans.each { |plan| format_plan(plan) } if coverage.positive? + log_skipped_summary(coverage_result) end private attr_reader :out, :verbose, :focused, :logger private - def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity + def format_plan(plan) logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage}%" return if plan.done? && !verbose @@ -32,9 +35,7 @@ def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity focused_route = requested_routes_count <= 1 && focused plan.routes.each do |route| - next if route.finished? && !verbose - - next if route.requests.none?(&:requested?) && focused_route + next unless report_route?(route, focused_route) format_requests(route.requests) @@ -42,9 +43,19 @@ def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity end end + def report_route?(route, focused_route) + return verbose if route.skipped? + return false if route.finished? && !verbose + return false if focused_route && route.requests.none?(&:requested?) + + true + end + def format_requests(requests) requests.each do |request| - if request.finished? + if request.skipped? + log_skipped "⚠ #{request_label(request)} – Skipped!" if verbose + elsif request.finished? log_success "✓ #{request_label(request)}" else log_error "❌ #{request_label(request)} – #{explain_unfinished_request(request)}" @@ -54,7 +65,9 @@ def format_requests(requests) def format_responses(responses) responses.each do |response| - if response.finished? + if response.skipped? + log_skipped " #{response_label(response)} – Skipped!" if verbose + elsif response.finished? log_success " ✓ #{response_label(response)}" if verbose else log_error " ❌ #{response_label(response)} – #{explain_unfinished_response(response)}" @@ -93,6 +106,10 @@ def log_success(msg) logger.info "\e[32m#{msg}\e[0m" end + def log_skipped(msg) + logger.info "\e[33m#{msg}\e[0m" + end + def log_error(msg) logger.error "\e[31m#{msg}\e[0m" end diff --git a/spec/test/coverage/html_reporter_spec.rb b/spec/test/coverage/html_reporter_spec.rb index de8f8ed9..fa40a674 100644 --- a/spec/test/coverage/html_reporter_spec.rb +++ b/spec/test/coverage/html_reporter_spec.rb @@ -20,6 +20,11 @@ } } } + }, + '/internal' => { + 'get' => { + 'responses' => { '200' => { 'description' => 'ok' } } + } } } } @@ -116,6 +121,69 @@ def run_reporter(result, **opts) end end + context 'with skipped requests and responses' do + let(:plan) do + OpenapiFirst::Test::Coverage::Plan.for( + oad, + skip_response: ->(res) { res.status == '400' }, + skip_route: ->(path, _method) { path == '/internal' } + ) + end + + it 'renders a toggle for skipped requests' do + html, = run_reporter(build_result(plan)) + expect(html).to include('id="filter-skipped-requests"') + expect(html).to include('Skipped requests') + end + + it 'shows the number of skipped requests and responses' do + html, = run_reporter(build_result(plan)) + expect(html).to include('1 request(s) and 2 response(s) skipped') + end + + it 'marks a skipped route as skipped instead of uncovered' do + html, = run_reporter(build_result(plan)) + expect(html).to include('class="route is-skipped"') + expect(html).to include('/internal') + expect(html).to include('request-status skipped') + end + + it 'lists the skipped request of a skipped route' do + html, = run_reporter(build_result(plan)) + expect(html).to include('Skipped, not covered by any test') + end + + it 'lists a skipped response of a route that is not skipped' do + html, = run_reporter(build_result(plan)) + expect(html).to include('class="skipped"') + expect(html).to include('application/problem+json') + end + + it 'does not count skipped responses as uncovered' do + plan.track_request(valid_request) + plan.track_response(valid_200_response) + html, = run_reporter(build_result(plan)) + expect(html).to include('class="route is-covered"') + expect(html).not_to include('response(s) not covered') + expect(html).to include('1 response(s) skipped') + end + + it 'warns that a request was skipped entirely' do + _, logger = run_reporter(build_result(plan)) + expect(logger).to have_received(:info).with('API coverage skipped 1 request(s) and 2 response(s).') + expect(logger).to have_received(:warn).with(include('skipped entirely')) + end + end + + context 'without any skipped requests or responses' do + it 'does not mention skipped requests or responses' do + html, logger = run_reporter(build_result(plan)) + expect(html).not_to include('class="skipped-summary"') + expect(logger).not_to have_received(:info).with(include('skipped')) + expect(logger).not_to have_received(:warn).with(include('skipped')) + end + end + context 'with no plans registered' do it 'writes a warning page' do result = OpenapiFirst::Test::Coverage::Result.new(plans: [], coverage: 0) diff --git a/spec/test/coverage/plan_spec.rb b/spec/test/coverage/plan_spec.rb index 99c69ff6..3c72c7ee 100644 --- a/spec/test/coverage/plan_spec.rb +++ b/spec/test/coverage/plan_spec.rb @@ -168,6 +168,54 @@ expect(plan.tasks.count(&:finished?)).to eq(0) end + describe 'skipped requests and responses' do + it 'keeps skipped responses in the routes and counts them' do + plan = described_class.for(oad, skip_response: ->(res) { res.status == '200' }) + route = plan.routes.first + + expect(route).not_to be_skipped + expect(route.responses.map(&:status)).to eq(%w[200 4XX]) + expect(route.skipped_responses.map(&:status)).to eq(['200']) + expect(route.tracked_responses.map(&:status)).to eq(['4XX']) + expect(plan.skipped_requests_count).to eq(0) + expect(plan.skipped_responses_count).to eq(1) + end + + it 'keeps a skipped route with all its requests and responses' do + plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true }) + route = plan.routes.first + + expect(route).to be_skipped + expect(route.requests.map(&:skipped?)).to eq([true]) + expect(route.responses.map(&:skipped?)).to eq([true, true]) + expect(route.tracked_requests).to be_empty + expect(route.tracked_responses).to be_empty + expect(plan.skipped_requests_count).to eq(1) + expect(plan.skipped_responses_count).to eq(2) + end + + it 'excludes skipped tasks from coverage' do + plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true }) + + expect(plan.tasks).to be_empty + expect(plan).to be_done + end + + it 'does not track a skipped request' do + plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true }) + + plan.track_request(valid_request) + + expect(plan.routes.first.requests.first).not_to be_requested + end + + it 'is not finished when the route is skipped' do + plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true }) + + expect(plan.routes.first).not_to be_finished + end + end + context 'with skip_response option' do let(:plan) do skip_response = ->(response) { response.status == '4XX' } diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 55a54a5f..9d2846b6 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -143,7 +143,8 @@ def call(_env) test.skip_coverage { |path, request_method| path == '/pets' && request_method == 'POST' } end route_tasks = described_class::Coverage.result.plans.first.routes - expect(route_tasks.map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets/{petId}', 'GET']]) + expect(route_tasks.select(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'POST']]) + expect(route_tasks.reject(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets/{petId}', 'GET']]) end it 'can skip_coverage for paths' do @@ -152,7 +153,19 @@ def call(_env) test.skip_coverage { |path| path == '/pets' } end route_tasks = described_class::Coverage.result.plans.first.routes - expect(route_tasks.map { |route| [route.path, route.request_method] }).to eq([['/pets/{petId}', 'GET']]) + expect(route_tasks.select(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets', 'POST']]) + expect(route_tasks.reject(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets/{petId}', 'GET']]) + end + + it 'reports the number of skipped requests and responses' do + described_class.setup do |test| + test.register('./spec/data/petstore.yaml') + test.skip_coverage { |path, request_method| path == '/pets' && request_method == 'POST' } + test.skip_response_coverage { |response| response.status == 'default' } + end + result = described_class::Coverage.result + expect(result.skipped_requests_count).to eq(1) + expect(result.skipped_responses_count).to eq(3) end it 'is okay if no block is given if an OAD is registered ' do