From 2fae4a0a30da5882c08e1ebaad355064574a210c Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Thu, 11 Aug 2016 14:45:51 +0800 Subject: [PATCH] Adding test for formatter I have added database cleaner as for formatter spec it was trying to use inbuild transactional fixture flag, which was failing as it couldn't find any transaction to rollback. Hence switched to using database_cleaner gem for using truncation as strategy --- .rspec | 1 + Gemfile | 1 + Gemfile.lock | 2 + spec/dummy/spec/rails_helper.rb | 13 +- spec/rails_helper.rb | 14 +- spec/spec_helper.rb | 2 + spec/support/formatter_support.rb | 340 +++++++++++++++++++++ spec/swagger_rails/rspec/formatter_spec.rb | 79 +++++ 8 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 spec/support/formatter_support.rb create mode 100644 spec/swagger_rails/rspec/formatter_spec.rb diff --git a/.rspec b/.rspec index 83e16f8..2cfeec5 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ --color --require spec_helper +--order rand \ No newline at end of file diff --git a/Gemfile b/Gemfile index 8235868..bf08a2e 100644 --- a/Gemfile +++ b/Gemfile @@ -22,4 +22,5 @@ end group :test do gem 'test-unit' + gem 'database_cleaner' end diff --git a/Gemfile.lock b/Gemfile.lock index 0d29bd9..2f2a066 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,6 +38,7 @@ GEM arel (3.0.3) builder (3.0.4) coderay (1.1.0) + database_cleaner (1.5.3) diff-lcs (1.2.5) erubis (2.7.0) generator_spec (0.9.3) @@ -121,6 +122,7 @@ PLATFORMS ruby DEPENDENCIES + database_cleaner generator_spec pry rspec-rails (~> 3.0) diff --git a/spec/dummy/spec/rails_helper.rb b/spec/dummy/spec/rails_helper.rb index 70c46e3..02d4fff 100644 --- a/spec/dummy/spec/rails_helper.rb +++ b/spec/dummy/spec/rails_helper.rb @@ -29,7 +29,7 @@ RSpec.configure do |config| # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. - config.use_transactional_fixtures = true + config.use_transactional_fixtures = false # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and @@ -51,6 +51,17 @@ RSpec.configure do |config| # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + config.before(:suite) do + DatabaseCleaner.strategy = :truncation + DatabaseCleaner.clean_with(:truncation) + end + + config.around(:each) do |example| + DatabaseCleaner.cleaning do + example.run + end + end + require 'swagger_rails/rspec/dsl' config.extend SwaggerRails::RSpec::DSL end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index dd86af5..f81a5fa 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -5,6 +5,7 @@ require File.expand_path('../dummy/config/environment', __FILE__) abort("The Rails environment is running in production mode!") if Rails.env.production? require 'spec_helper' require 'rspec/rails' +require 'database_cleaner' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in @@ -33,7 +34,7 @@ RSpec.configure do |config| # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. - config.use_transactional_fixtures = true + config.use_transactional_fixtures = false # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and @@ -49,4 +50,15 @@ RSpec.configure do |config| # The different available types are documented in the features, such as in # https://relishapp.com/rspec/rspec-rails/docs config.infer_spec_type_from_file_location! + + config.before(:suite) do + DatabaseCleaner.strategy = :truncation + DatabaseCleaner.clean_with(:truncation) + end + + config.around(:each) do |example| + DatabaseCleaner.cleaning do + example.run + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 913e28a..4e08ab5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f } + # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb new file mode 100644 index 0000000..1d96824 --- /dev/null +++ b/spec/support/formatter_support.rb @@ -0,0 +1,340 @@ +# Refer: https://github.com/rspec/rspec-core/blob/cf8dfdc76586a9bb09da0bd03f0da3f2cf8fc580/spec/support/formatter_support.rb + +module FormatterSupport + def run_example_specs_with_formatter(formatter_option, options={}, &block) + output = run_rspec_with_formatter(formatter_option, options.merge(:extra_options => ["spec/rspec/core/resources/formatter_specs.rb"]), &block) + + return output unless options.fetch(:normalize_output, true) + output = normalize_durations(output) + + caller_line = RSpec::Core::Metadata.relative_path(caller.first) + output.lines.reject do |line| + # remove the direct caller as that line is different for the summary output backtraces + line.include?(caller_line) || + + # ignore scirpt/rspec_with_simplecov because we don't usually have it locally but + # do have it on travis + line.include?("script/rspec_with_simplecov") || + + # this line varies a bit depending on how you run the specs (via `rake` vs `rspec`) + line.include?('/exe/rspec:') + end.join + end + + def run_rspec_with_formatter(formatter, options={}) + extra_options = options.fetch(:extra_options) { [] } + + options = RSpec::Core::ConfigurationOptions.new([ + "--format", formatter, "--order", "defined", *extra_options + ]) + + err, out = StringIO.new, StringIO.new + err.set_encoding("utf-8") if err.respond_to?(:set_encoding) + + runner = RSpec::Core::Runner.new(options) + configuration = runner.configuration + configuration.backtrace_formatter.exclusion_patterns << /rspec_with_simplecov/ + configuration.backtrace_formatter.inclusion_patterns = [] + + yield runner if block_given? + + runner.run(err, out) + out.string + end + + def normalize_durations(output) + output.gsub(/(?:\d+ minutes? )?\d+(?:\.\d+)?(s| seconds?)/) do |dur| + suffix = $1 == "s" ? "s" : " seconds" + "n.nnnn#{suffix}" + end + end + + if RUBY_VERSION.to_f < 1.9 + def expected_summary_output_for_example_specs + <<-EOS.gsub(/^\s+\|/, '').chomp + |Pending: (Failures listed here are expected and do not affect your suite's status) + | + | 1) pending spec with no implementation is pending + | # Not yet implemented + | # ./spec/rspec/core/resources/formatter_specs.rb:11 + | + | 2) pending command with block format with content that would fail is pending + | # No reason given + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:18 + | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' + | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:14 + | # ./spec/support/sandboxing.rb:7 + | + |Failures: + | + | 1) pending command with block format behaves like shared is marked as pending but passes FIXED + | Expected pending 'No reason given' to fail. No Error was raised. + | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 + | # ./spec/rspec/core/resources/formatter_specs.rb:4 + | + | 2) failing spec fails + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:33 + | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' + | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:14 + | # ./spec/support/sandboxing.rb:7 + | + | 3) a failing spec with odd backtraces fails with a backtrace that has no file + | Failure/Error: Unable to find (erb) to read failed line + | + | RuntimeError: + | foo + | # (erb):1 + | + | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file + | Failure/Error: Unable to find /foo.html.erb to read failed line + | + | Exception: + | Exception + | # /foo.html.erb:1:in `
': foo (RuntimeError) + | + | 5) a failing spec with odd backtraces with a `nil` backtrace raises + | Failure/Error: Unable to find matching line from backtrace + | + | RuntimeError: + | boom + | + |Finished in n.nnnn seconds (files took n.nnnn seconds to load) + |8 examples, 5 failures, 2 pending + | + |Failed examples: + | + |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes + |rspec ./spec/rspec/core/resources/formatter_specs.rb:32 # failing spec fails + |rspec ./spec/rspec/core/resources/formatter_specs.rb:38 # a failing spec with odd backtraces fails with a backtrace that has no file + |rspec ./spec/rspec/core/resources/formatter_specs.rb:44 # a failing spec with odd backtraces fails with a backtrace containing an erb file + |rspec ./spec/rspec/core/resources/formatter_specs.rb:62 # a failing spec with odd backtraces with a `nil` backtrace raises + EOS + end + else + def expected_summary_output_for_example_specs + <<-EOS.gsub(/^\s+\|/, '').chomp + |Pending: (Failures listed here are expected and do not affect your suite's status) + | + | 1) pending spec with no implementation is pending + | # Not yet implemented + | # ./spec/rspec/core/resources/formatter_specs.rb:11 + | + | 2) pending command with block format with content that would fail is pending + | # No reason given + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:18:in `block (3 levels) in ' + | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' + | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | + |Failures: + | + | 1) pending command with block format behaves like shared is marked as pending but passes FIXED + | Expected pending 'No reason given' to fail. No Error was raised. + | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 + | # ./spec/rspec/core/resources/formatter_specs.rb:4 + | + | 2) failing spec fails + | Failure/Error: expect(1).to eq(2) + | + | expected: 2 + | got: 1 + | + | (compared using ==) + | # ./spec/rspec/core/resources/formatter_specs.rb:33:in `block (2 levels) in ' + | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' + | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | + | 3) a failing spec with odd backtraces fails with a backtrace that has no file + | Failure/Error: ERB.new("<%= raise 'foo' %>").result + | + | RuntimeError: + | foo + | # (erb):1:in `
' + | # ./spec/rspec/core/resources/formatter_specs.rb:41:in `block (2 levels) in ' + | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' + | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' + | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' + | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | + | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file + | Failure/Error: Unable to find /foo.html.erb to read failed line + | + | Exception: + | Exception + | # /foo.html.erb:1:in `
': foo (RuntimeError) + | + | 5) a failing spec with odd backtraces with a `nil` backtrace raises + | Failure/Error: Unable to find matching line from backtrace + | + | RuntimeError: + | boom + | + |Finished in n.nnnn seconds (files took n.nnnn seconds to load) + |8 examples, 5 failures, 2 pending + | + |Failed examples: + | + |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes + |rspec ./spec/rspec/core/resources/formatter_specs.rb:32 # failing spec fails + |rspec ./spec/rspec/core/resources/formatter_specs.rb:38 # a failing spec with odd backtraces fails with a backtrace that has no file + |rspec ./spec/rspec/core/resources/formatter_specs.rb:44 # a failing spec with odd backtraces fails with a backtrace containing an erb file + |rspec ./spec/rspec/core/resources/formatter_specs.rb:62 # a failing spec with odd backtraces with a `nil` backtrace raises + EOS + end + end + + def send_notification type, notification + reporter.notify type, notification + end + + def reporter + @reporter ||= setup_reporter + end + + def setup_reporter(*streams) + config.add_formatter described_class, *streams + @formatter = config.formatters.first + @reporter = config.reporter + end + + def setup_profiler + config.profile_examples = true + reporter.setup_profiler + end + + def formatter_output + @formatter_output ||= StringIO.new + end + + def config + @configuration ||= + begin + config = RSpec::Core::Configuration.new + config.output_stream = formatter_output + config + end + end + + def configure + yield config + end + + def formatter + @formatter ||= + begin + setup_reporter + @formatter + end + end + + def new_example(metadata = {}) + metadata = metadata.dup + result = RSpec::Core::Example::ExecutionResult.new + result.started_at = ::Time.now + result.record_finished(metadata.delete(:status) { :passed }, ::Time.now) + result.exception = Exception.new if result.status == :failed + + instance_double(RSpec::Core::Example, + :description => "Example", + :full_description => "Example", + :example_group => group, + :execution_result => result, + :location => "", + :location_rerun_argument => "", + :metadata => { + :shared_group_inclusion_backtrace => [] + }.merge(metadata) + ) + end + + def examples(n) + Array.new(n) { new_example } + end + + def group + group = class_double "RSpec::Core::ExampleGroup", :description => "Group" + allow(group).to receive(:parent_groups) { [group] } + group + end + + def start_notification(count) + ::RSpec::Core::Notifications::StartNotification.new count + end + + def stop_notification + ::RSpec::Core::Notifications::ExamplesNotification.new reporter + end + + def example_notification(specific_example = new_example) + ::RSpec::Core::Notifications::ExampleNotification.for specific_example + end + + def group_notification group_to_notify = group + ::RSpec::Core::Notifications::GroupNotification.new group_to_notify + end + + def message_notification(message) + ::RSpec::Core::Notifications::MessageNotification.new message + end + + def null_notification + ::RSpec::Core::Notifications::NullNotification + end + + def seed_notification(seed, used = true) + ::RSpec::Core::Notifications::SeedNotification.new seed, used + end + + def failed_examples_notification + ::RSpec::Core::Notifications::ExamplesNotification.new reporter + end + + def summary_notification(duration, examples, failed, pending, time) + ::RSpec::Core::Notifications::SummaryNotification.new duration, examples, failed, pending, time + end + + def profile_notification(duration, examples, number) + ::RSpec::Core::Notifications::ProfileNotification.new duration, examples, number, reporter.instance_variable_get('@profiler').example_groups + end + +end + +if RSpec::Support::RubyFeatures.module_prepends_supported? + module RSpec::Core + class Reporter + module EnforceRSpecNotificationsListComplete + def notify(event, *args) + return super if caller_locations(1, 1).first.label =~ /publish/ + return super if RSPEC_NOTIFICATIONS.include?(event) + + raise "#{event.inspect} must be added to `RSPEC_NOTIFICATIONS`" + end + end + + prepend EnforceRSpecNotificationsListComplete + end + end +end \ No newline at end of file diff --git a/spec/swagger_rails/rspec/formatter_spec.rb b/spec/swagger_rails/rspec/formatter_spec.rb new file mode 100644 index 0000000..f1cc06b --- /dev/null +++ b/spec/swagger_rails/rspec/formatter_spec.rb @@ -0,0 +1,79 @@ +require 'swagger_rails/rspec/formatter' +require 'swagger_rails/rspec/dsl' +require 'rails_helper' + +RSpec.describe ::SwaggerRails::RSpec::Formatter do + include FormatterSupport + + def group + RSpec.describe('Ping API', swagger_doc: 'v1_api.json') do + path('/ping') do + post('checks if site is alive') do + consumes('application/json') + produces('application/json') + operation_description('A very long description') + response('200', '(OK) Site up and running') do + run_test! + end + end + end + end + end + + def send_notification_for_all_child_groups(group) + send_notification :example_group_finished, group_notification(group) + group.children.each do |child| + return if child.class.name == 'ValidRequest' + send_notification_for_all_child_groups(child) + end + end + + def swaggerize(*groups) + groups.each do |group| + group.run(reporter) + send_notification_for_all_child_groups group + end + end + + before(:each) do + swagger_root = (Rails.root + 'swagger').to_s + SwaggerRails::Configuration.new.tap { |c| c.swagger_root = swagger_root } + allow(RSpec.configuration).to receive(:swagger_docs).and_return({ 'v1_api.json' => { swagger: '2.0', + info: { title: 'API V1', + version: 'v1' } } }) + allow(RSpec.configuration).to receive(:swagger_root).and_return(swagger_root) + @formatter = SwaggerRails::RSpec::Formatter.new(StringIO.new) + end + + describe '#new' do + it 'should initialize the swagger_root' do + expect(@formatter.instance_variable_get(:@swagger_root)).to eq((Rails.root + 'swagger').to_s) + expect(@formatter.instance_variable_get(:@swagger_docs)).to eq({"v1_api.json"=>{:swagger=>"2.0", :info=>{:title=>"API V1", :version=>"v1"}}}) + end + end + + describe '#example_group_finished' do + before do + swaggerize(group) + end + + it "should print 'Generating Swagger Docs ...'" do + expect(formatter_output.string).to eq("Generating Swagger Docs ...\n") + end + + it 'should update the swagger_doc instance variable' do + expect(@formatter.instance_variable_get(:@swagger_docs) + ).to eq({ + 'v1_api.json' => { :swagger => '2.0', + :info => { :title => 'API V1', + :version => 'v1' }, + :paths => { '/ping' => { :post => { :tags => ['Ping API'], + :summary => 'checks if site is alive', + :description => 'A very long description', + :consumes => ['application/json'], + :produces => ['application/json'], + :parameters => [], + :responses => { '200' => { :description => '(OK) Site up and running' } } } } } } }) + end + end +end