Running RSpec in dry mode and RSpec formatters
Outline:
- Cloning of repository and building app
- RSpec in dry-run mode
- RSpec formatters
- Custom formatters
Cloning and building the app
repo_url = "github.com/#{repo.owner}/#{repo.name}.git"
system "git clone https://#{access_token}@#{repo_url}"
Cloning and building the app
Run bundle inside the cloned application directory as below?
./bin/bundle install
Where gems are extracted?
We can not interfere in the main app environment !!!
Cloning and building the app
Are you sure bundle uses the correct Gemfile and installs gems for appropriate applications?
./bin/bundle install --path path/to/extracted/gems
NOT !!!
Bundle uses Gemfile from the main app
Bundle installs gems for the main app
Cloning and building the app
Bundle uses correct Gemfile
Bundle installs gems for the main app
./bin/bundle install --path path/to/extracted/gems
--gemfile path/to/Gemfile
Cloning and building the app
SOLUTION:
Bundler.with_clean_env do
FileUtils.cd("#{Rails.root}/path/to/gh_repository") do
system "./bin/bundle install --path path/to/extracted/gems"
end
end
RSpec in dry-run mode
RSpec in dry-run mode
Use the --dry-run option to have RSpec print your suite's formatter output without running any examples or hooks."
RSpec in dry-run mode
Bundler.with_clean_env do
FileUtils.cd("#{Rails.root}/path/to/gh_repository") do
System "./bin/bundle exec rspec spec --dry-run"
end
end
RSpec requires a current database also in dry-run mode, but it does not use its !!!
Migrations are pending. To resolve this issue, run:
bin/rake db:migrate RAILS_ENV=test
Is the creation of a database specifically for this reason it is reasonable?
Of course NOT !!!
SOLUTION:
# support_for_rspec.rb
module ActiveRecord
class Migration
def self.maintain_test_schema!
true
end
end
end
./bin/bundle exec rspec spec --dry-run
--require path/to/support_for_rspec.rb
Result:
- RSpec runs the tests and displays it in the default format
Problems to be solved:
- create a formatter, which adapts the RSpec output to our needs
- intercept output stream
Formatters in RSpec
--format option
-
progress - default
output: ....F.....*.... -
documentation (group and example names)
output:
something
does something that passes
does something that fails (FAILED - 1)
does something that is pending (PENDING: Not Yet Implemented - html
-
json
output:
{ "examples": [
{
"description": "should require title to be set",
"full_description": "Article validations should require title to be set",
"status": "passed",
"file_path": "./spec/models/article_spec.rb",
"line_number": 5,
"run_time": 0.117204642
},{
"description": "should require content to be set",
"full_description": "Article validations should require content to be set",
"status": "passed",
"file_path": "./spec/models/article_spec.rb",
"line_number": 6,
"run_time": 0.010326601
}]
summary": {
"duration": 1.444473286,
"example_count": 2,
"failure_count": 0,
"pending_count": 0
},
"summary_line": "2 examples, 0 failures"
}
Custom formatters
# RSpec::Core::Formatters::JsonFormatter
class JsonFormatter < BaseFormatter
attr_reader :output_hash
def initialize(output)
super
@output_hash = {}
end
def message(message)
(@output_hash[:messages] ||= []) << message
end
def dump_summary(duration, example_count, failure_count, pending_count)
super(duration, example_count, failure_count, pending_count)
@output_hash[:summary] = {
:duration => duration,
:example_count => example_count,
:failure_count => failure_count,
:pending_count => pending_count
}
@output_hash[:summary_line] = summary_line(example_count, failure_count, pending_count)
end
def summary_line(example_count, failure_count, pending_count)
summary = pluralize(example_count, "example")
summary << ", " << pluralize(failure_count, "failure")
summary << ", #{pending_count} pending" if pending_count > 0
summary
end
def stop
super
@output_hash[:examples] = examples.map do |example|
{
:description => example.description,
:full_description => example.full_description,
:status => example.execution_result[:status],
# :example_group,
# :execution_result,
:file_path => example.metadata[:file_path],
:line_number => example.metadata[:line_number],
}.tap do |hash|
if e=example.exception
hash[:exception] = {
:class => e.class.name,
:message => e.message,
:backtrace => e.backtrace,
}
end
end
end
end
def close
output.write @output_hash.to_json
output.close if IO === output && output != $stdout
end
end
class PercentFormatter < RSpec::Core::Formatters::JsonFormatter
def dump_summary(duration, example_count, failure_count, pending_count)
super(duration, example_count, failure_count, pending_count)
@output_hash[:summary] = {
:percent_of_success => “#{(example_count - failure_count) / example_count * 100}%”
:duration => duration,
:example_count => example_count,
:failure_count => failure_count,
:pending_count => pending_count
}
@output_hash[:summary_line] = summary_line(example_count, failure_count, pending_count)
end
end
./bin/bundle exec rspec spec --dry-run
--require path/to/support_for_rspec.rb path/to/percent_formater.rb
--format PercentFormatter
class JsonFormatterWithTreeStructure < RSpec::Core::Formatters::JsonFormatter
RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :close
def initialize(output)
super(output)
@example_nodes = []
end
private
def format_example(example)
@example_nodes = []
set_example_nodes(example.metadata)
build_tree_structure
end
def set_example_nodes(example)
@example_nodes << {
description: example[:description],
full_description: example[:full_description],
file_path: example[:file_path],
line_number: example[:line_number],
example_type: example[:type]
}
if example[:parent_example_group]
set_example_nodes example[:parent_example_group]
elsif example[:example_group] && !(example[:example_group].to_h == example)
set_example_nodes example[:example_group]
end
end
def build_tree_structure
@example_nodes.map.with_index do |node, index|
@example_nodes[index+1] ? @example_nodes[index+1][:child] = node : node
end.last
end
end
https://www.relishapp.com/rspec/rspec-core/docs/formatters/custom-formatters
Bundler.with_clean_env do
FileUtils.cd("#{Rails.root}/path/to/gh_repository") do
required = "--require #{required_support_file} --require #{formatter}"
cmd = "./bin/bundle exec rspec spec --dry-run #{required} --format JsonFormatterWithTreeStructure"
stdin, stdout, stderr = Open3.popen3(cmd)
@output_tests_lists = JSON.parse(stdout.read.partition('{').last(2).join)
end
end
Running RSpec in dry-run mode and RSpec formatters
By Przemek Sienkowski
Running RSpec in dry-run mode and RSpec formatters
- 996