class Mutest::Integration::Rspec
Rspec
integration
This looks so complicated, because rspec:
-
Keeps its state global in RSpec.world and lots of other places
-
There is no API to “just run a subset of examples”, the examples need to be selected in-place via mutating the `RSpec.filtered_examples` data structure
-
Does not maintain a unique identification for an example, aside the instances of `RSpec::Core::Example` objects itself. For that reason identifying examples by:
-
full description
-
location
Is NOT enough. It would not be unique. So we add an “example index” for unique reference.
-
Constants
- ALL_EXPRESSION
- CLI_OPTIONS
- EXIT_SUCCESS
- EXPRESSION_CANDIDATE
- LOCATION_DELIMITER
- TEST_ID_FORMAT
Public Class Methods
Initialize rspec integration
@return [undefined]
# File lib/mutest/integration/rspec.rb, line 33 def initialize(*) super @output = StringIO.new @runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new(CLI_OPTIONS)) @world = RSpec.world end
Public Instance Methods
Available tests
@return [Enumerable<Test>]
# File lib/mutest/integration/rspec.rb, line 73 def all_tests all_tests_index.keys end
Run a collection of tests
@param [Enumerable<Mutest::Test>] tests
@return [Result::Test]
rubocop:disable MethodLength
# File lib/mutest/integration/rspec.rb, line 56 def call(tests) examples = tests.map(&all_tests_index.method(:fetch)) filter_examples(&examples.method(:include?)) start = Time.now passed = @runner.run_specs(@world.ordered_example_groups).equal?(EXIT_SUCCESS) @output.rewind Result::Test.new( output: @output.read, passed: passed, runtime: Time.now - start, tests: tests ) end
Setup rspec integration
@return [self]
# File lib/mutest/integration/rspec.rb, line 43 def setup @runner.setup($stderr, @output) self end
Private Instance Methods
Available rspec examples
@return [Array<String, RSpec::Core::Example>]
# File lib/mutest/integration/rspec.rb, line 129 def all_examples @world.example_groups.flat_map(&:descendants).flat_map(&:examples).select do |example| example.metadata.fetch(:mutest, true) end end
Index of available tests
@return [Hash<Test, RSpec::Core::Example>]
# File lib/mutest/integration/rspec.rb, line 83 def all_tests_index all_examples.each_with_index.each_with_object({}) do |(example, example_index), index| index[parse_example(example, example_index)] = example end end
Filter examples
@param [#call] predicate
@return [undefined]
# File lib/mutest/integration/rspec.rb, line 140 def filter_examples(&predicate) @world.filtered_examples.each_value do |examples| examples.keep_if(&predicate) end end
Parse example into test
@param [RSpec::Core::Example] example @param [Integer] index
@return [Test]
# File lib/mutest/integration/rspec.rb, line 96 def parse_example(example, index) metadata = example.metadata id = format( TEST_ID_FORMAT, index: index, location: metadata.fetch(:location), description: metadata.fetch(:full_description) ) Test.new( expression: parse_expression(metadata), id: id ) end
Parse metadata into expression
@param [RSpec::Core::Example::MetaData] metadata
@return [Expression]
# File lib/mutest/integration/rspec.rb, line 117 def parse_expression(metadata) if metadata.key?(:mutest_expression) expression_parser.(metadata.fetch(:mutest_expression)) else match = EXPRESSION_CANDIDATE.match(metadata.fetch(:full_description)) expression_parser.try_parse(match.captures.first) || ALL_EXPRESSION end end