Skip to content

Commit

Permalink
Add test minitest integration
Browse files Browse the repository at this point in the history
[fix #92]
  • Loading branch information
mbj committed Sep 22, 2015
1 parent cd47031 commit e4704c7
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 1 deletion.
154 changes: 154 additions & 0 deletions lib/mutant/integration/minitest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# frozen_string_literal: true

require 'minitest'

# monkey patch for minitest
module Minitest
# Prevent autorun from running tests when the VM closes
#
# Mutant needs control about the exit status of the VM and
# the moment of test execution
#
# @api private
#
# @return [nil]
def self.autorun
end

end # Minitest

module Mutant
class Integration
# Minitest integration
class Minitest < self
TEST_FILE_PATTERN = './test/**/{test_*,*_test}.rb'
IDENTIFICATION_FORMAT = 'minitest:%s#%s'

private_constant(*constants(false))

# Compose a runnable with test method
#
# This looks actually like a missing object on minitest implementation.
class TestCase
include Adamantium, Concord.new(:klass, :test_method)

# Identification string
#
# @return [String]
def identification
IDENTIFICATION_FORMAT % [klass, test_method]
end
memoize :identification

# Run test case
#
# @param [Object] reporter
#
# @return [Boolean]
def call(reporter)
::Minitest::Runnable.run_one_method(klass, test_method, reporter)
reporter.passed?
end

# Cover expression syntaxes
#
# @return [Array<String>]
def expression_syntax
klass.cover_expression
end

end # TestCase

private_constant(*constants(false))

# Setup integration
#
# @return [self]
def setup
Pathname.glob(TEST_FILE_PATTERN)
.map(&:to_s)
.each(&method(:require))

self
end

# Call test integration
#
# @param [Array<Tests>] tests
#
# @return [Result::Test]
#
# rubocop:disable MethodLength
def call(tests)
test_cases = tests.map(&all_tests_index.method(:fetch)).to_set

output = StringIO.new
reporter = ::Minitest::SummaryReporter.new(output)
start = Time.now

passed = test_cases.all? { |test| test.call(reporter) }
output.rewind

Result::Test.new(
passed: passed,
tests: tests,
output: output.read,
runtime: Time.now - start
)
end

# All tests exposed by this integration
#
# @return [Array<Test>]
def all_tests
all_tests_index.keys
end
memoize :all_tests

private

# The index of all tests to runnable test cases
#
# @return [Hash<Test,TestCase>]
def all_tests_index
all_test_cases.each_with_object({}) do |test_case, index|
index[construct_test(test_case)] = test_case
end
end
memoize :all_tests_index

# Construct test from test case
#
# @param [TestCase]
#
# @return [Test]
def construct_test(test_case)
Test.new(
id: test_case.identification,
expression: config.expression_parser.(test_case.expression_syntax)
)
end

# All minitest test cases
#
# Intentional utility method.
#
# @return [Array<TestCase>]
def all_test_cases
::Minitest::Runnable.runnables.flat_map(&method(:test_case))
end

# Turn a minitest runnable into its test cases
#
# Intentional utility method.
#
# @param [Object] runnable
#
# @return [Array<TestCase>]
def test_case(runnable)
runnable.runnable_methods.map { |method| TestCase.new(runnable, method) }
end

end # Minitest
end # Integration
end # Mutant
22 changes: 22 additions & 0 deletions mutant-minitest.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require File.expand_path('../lib/mutant/version', __FILE__)

Gem::Specification.new do |gem|
gem.name = 'mutant-minitest'
gem.version = Mutant::VERSION.dup
gem.authors = ['Markus Schirp']
gem.email = %w[[email protected]]
gem.description = 'Minitest integration for mutant'
gem.summary = gem.description
gem.homepage = 'https://github.com/mbj/mutant'
gem.license = 'MIT'

gem.require_paths = %w[lib]
gem.files = `git ls-files -- lib/mutant/integration/minitest.rb`.split("\n")
gem.test_files = `git ls-files -- spec/integration/mutant/minitest.rb`.split("\n")
gem.extra_rdoc_files = %w[TODO LICENSE]

gem.add_runtime_dependency('mutant', "~> #{gem.version}")
gem.add_runtime_dependency('minitest', '~> 5.5')
end
10 changes: 10 additions & 0 deletions spec/integration/mutant/minitest_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
RSpec.describe 'minitest integration', mutant: false do

let(:base_cmd) { 'bundle exec mutant -I test -I lib --require test_app --use minitest' }

context 'Minitest 5.5.0' do
let(:gemfile) { 'Gemfile.minitest-stdlib' }

it_behaves_like 'framework integration'
end
end
2 changes: 1 addition & 1 deletion spec/shared/framework_integration_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ def system_with_gemfile(*command)
specify 'it allows to exclude mutations' do
cli = <<-CMD.split("\n").join(' ')
#{base_cmd}
--ignore-subject TestApp::Literal#uncovered_string
TestApp::Literal#string
TestApp::Literal#uncovered_string
--ignore-subject TestApp::Literal#uncovered_string
CMD
expect(system_with_gemfile(cli)).to be(true)
end
Expand Down
6 changes: 6 additions & 0 deletions test_app/Gemfile.minitest-stdlib
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'

gem 'minitest', '~> 5.5'
gem 'mutant', path: '../'
gem 'mutant-minitest', path: '../'
gem 'adamantium'
19 changes: 19 additions & 0 deletions test_app/test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')

require 'test_app'
require 'minitest/autorun'

# require spec support files and shared behavior
Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].sort.each do |file|
require file
end

class TestAppTest < Minitest::Test
def self.cover(expression)
@expression = expression
end

def self.cover_expression
@expression or fail "Cover expression for #{self} is not specified"
end
end
19 changes: 19 additions & 0 deletions test_app/test/unit/test_app/literal_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'test_helper'

class TestApp::LiteralTest < TestAppTest
cover 'TestApp::Literal*'

def test_command
object = ::TestApp::Literal.new
subject = object.command('x')

assert_equal object, subject
end

def test_string
object = ::TestApp::Literal.new
subject = object.string

assert_equal 'string', subject
end
end

0 comments on commit e4704c7

Please sign in to comment.