Merge pull request #94 from mbj/flexible-rspec

WIP - Flexible rspec
This commit is contained in:
Markus Schirp 2013-08-04 14:55:43 -07:00
commit f3e1a76914
27 changed files with 295 additions and 647 deletions

View file

@ -6,6 +6,8 @@ gemspec
gem 'mutant', path: '.'
gem 'rspec-core', path: '../rspec-core'
group :development, :test do
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
end

View file

@ -17,10 +17,7 @@ any ruby engine that supports POSIX-fork(2) semantics.
Only rspec2 is supported currently. This is subject to change.
It is easy to write a mutation killer for other test/spec frameworks than rspec2.
Just create your own Mutant::Killer subclass, and make sure I get a PR!
See this [ASCII-Cast](http://ascii.io/a/1707) for mutant in action! (v0.2.1)
It is easy to write a mutation killer/strategy for other test/spec frameworks than rspec2.
Projects using Mutant
---------------------

View file

@ -1,3 +1,3 @@
---
threshold: 16
total_score: 758
total_score: 734

View file

@ -1,2 +1,2 @@
---
threshold: 20.5
threshold: 20.0

View file

@ -96,8 +96,7 @@ UncommunicativeMethodName:
accept: []
UncommunicativeModuleName:
enabled: true
exclude:
- Mutant::Strategy::Rspec::DM2
exclude: []
reject:
- !ruby/regexp /^.$/
- !ruby/regexp /[0-9]$/

View file

@ -19,6 +19,7 @@ require 'diff/lcs/hunk'
require 'rspec'
require 'anima'
require 'concord'
require 'rspec'
# Library namespace
module Mutant
@ -105,11 +106,7 @@ require 'mutant/killer/forking'
require 'mutant/killer/forked'
require 'mutant/strategy'
require 'mutant/strategy/static'
require 'mutant/strategy/method_expansion'
require 'mutant/strategy/rspec'
require 'mutant/strategy/rspec/dm2'
require 'mutant/strategy/rspec/dm2/lookup'
require 'mutant/strategy/rspec/dm2/lookup/method'
require 'mutant/runner'
require 'mutant/runner/config'
require 'mutant/runner/subject'

View file

@ -59,6 +59,7 @@ module Mutant
def config
Config.new(
:cache => @cache,
:zombie => @zombie,
:debug => debug?,
:matcher => matcher,
:filter => filter,
@ -154,26 +155,6 @@ module Mutant
@filters << klass.new(filter)
end
# Set fail fast
#
# @api private
#
# @return [undefined]
#
def set_fail_fast
@fail_fast = true
end
# Set debug mode
#
# @api private
#
# @return [undefined]
#
def set_debug
@debug = true
end
# Set strategy
#
# @param [Strategy] strategy
@ -204,9 +185,8 @@ module Mutant
builder.separator ''
builder.separator 'Strategies:'
builder.on('--zombie', 'Run mutant zombified')
add_strategies(builder)
add_environmental_options(builder)
add_options(builder)
end
@ -237,25 +217,48 @@ module Mutant
# Add strategies
#
# @param [Object]
# @param [Object] opts
#
# @return [undefined]
#
# @api private
#
def add_strategies(opts)
opts.on('--rspec-unit', 'executes all specs under ./spec/unit') do
set_strategy Strategy::Rspec::Unit
end.on('--rspec-full', 'executes all specs under ./spec') do
set_strategy Strategy::Rspec::Full
end.on('--rspec-dm2', 'executes spec/unit/$nesting/$method_spec.rb') do
set_strategy Strategy::Rspec::DM2
opts.separator ''
opts.separator 'Strategies:'
opts.on('--static-success', 'does succeed on all mutations') do
set_strategy Strategy::Static::Success.new
end
opts.on('--static-fail', 'does fail on all mutations') do
set_strategy Strategy::Static::Fail.new
end
opts.on('--rspec', 'kills mutations with rspec') do
set_strategy Strategy::Rspec.new
end
end
# Add environmental options
#
# @param [Object] opts
#
# @return [undefined]
#
# @api private
#
def add_environmental_options(opts)
opts.on('--zombie', 'Run mutant zombified') do
@zombie = true
end.on('-I', 'Add directory to $LOAD_PATH') do |directory|
$LOAD_PATH << directory
end.on('-r', '--require NAME', 'Require file with NAME') do |name|
require name
end
end
# Add options
#
# @param [Object]
# @param [Object] opts
#
# @return [undefined]
#
@ -266,18 +269,16 @@ module Mutant
opts.separator 'Options:'
opts.on('--version', 'Print mutants version') do |name|
puts "mutant-#{Mutant::VERSION}"
puts("mutant-#{Mutant::VERSION}")
Kernel.exit(0)
end.on('-r', '--require NAME', 'Require file with NAME') do |name|
require name
end.on('--code FILTER', 'Adds a code filter') do |filter|
add_filter Mutation::Filter::Code, filter
add_filter(Mutation::Filter::Code, filter)
end.on('--fail-fast', 'Fail fast') do
set_fail_fast
@fail_fast = true
end.on('-d', '--debug', 'Enable debugging output') do
set_debug
@debug = true
end.on_tail('-h', '--help', 'Show this message') do
puts opts
puts(opts)
exit
end
end

View file

@ -4,7 +4,8 @@ module Mutant
# The configuration of a mutator run
class Config
include Adamantium::Flat, Anima.new(
:cache, :debug, :strategy, :matcher, :filter, :reporter, :fail_fast
:cache, :debug, :strategy, :matcher, :filter,
:reporter, :fail_fast, :zombie
)
# Enumerate subjects

View file

@ -89,6 +89,20 @@ module Mutant
scope.name
end
# Return match prefixes
#
# @return [Enumerable<String>]
#
# @api private
#
def match_prefixes
names = name_nesting
names.length.downto(1).map do |last|
names[0...last].join('::')
end
end
memoize :match_prefixes
# Return scope wrapped by context
#
# @return [::Module|::Class]

View file

@ -98,6 +98,16 @@ module Mutant
@runtime = times.real
end
# Return subject
#
# @return [Subject]
#
# @api private
#
def subject
mutation.subject
end
# Run killer
#
# @return [true]

View file

@ -5,6 +5,20 @@ module Mutant
# Runner for rspec tests
class Rspec < self
# Noop reporter
module Reporter
%w(example example_group).each do |method_name|
%w(passed started failed finished pending).each do |state|
name = "#{method_name}_#{state}"
define_singleton_method(name) do |_subject|
self
end
end
end
freeze
end
private
# Run rspec test
@ -19,36 +33,68 @@ module Mutant
#
def run
mutation.insert
# TODO: replace with real streams from configuration
require 'stringio'
# Note: We assume interesting output from a failed rspec run is stderr.
rspec_err = StringIO.new
exit_code = ::RSpec::Core::Runner.run(cli_arguments, nil, rspec_err)
groups = example_groups
killed = !exit_code.zero?
if killed and mutation.should_survive?
rspec_err.rewind
puts "#{mutation.class} test failed."
puts 'RSpec stderr:'
puts rspec_err.read
unless groups
$stderr.puts "No rspec example groups found for: #{match_prefixes.join(', ')}"
return false
end
killed
example_groups.each do |group|
return true unless group.run(Reporter)
end
false
end
# Return command line arguments
# Return match prefixes
#
# @return [Array]
# @return [Enumerble<String>]
#
# @api private
#
def cli_arguments
%W(
--fail-fast
) + strategy.spec_files(mutation.subject)
def match_prefixes
subject.match_prefixes
end
# Return example groups
#
# @return [Array<RSpec::Example>]
#
# @api private
#
def example_groups
match_prefixes.each do |match_expression|
example_groups = find_with(match_expression)
return example_groups unless example_groups.empty?
end
nil
end
# Return example groups that match expression
#
# @param [String] match_expression
#
# @return [Enumerable<String>]
#
# @api private
#
def find_with(match_expression)
all_example_groups.select do |example_group|
example_group.description.start_with?(match_expression)
end
end
# Return all example groups
#
# @return [Enumerable<RSpec::Example>]
#
# @api private
#
def all_example_groups
strategy.example_groups
end
end # Rspec

View file

@ -10,6 +10,9 @@ module Mutant
handle(Mutant::Killer::Forked)
SUCCESS = '.'.freeze
FAILURE = 'F'.freeze
# Run printer
#
# @return [undefined]
@ -18,12 +21,14 @@ module Mutant
#
def run
if success?
char('.', Color::GREEN)
return
char(SUCCESS, Color::GREEN)
else
char(FAILURE, Color::RED)
end
char('F', Color::RED)
end
private
# Write colorized char
#
# @param [String] char
@ -37,6 +42,7 @@ module Mutant
output.write(colorize(color, char))
output.flush
end
end # Killer
end # Printer
end # CLI

View file

@ -6,24 +6,22 @@ module Mutant
class Strategy
include AbstractType, Adamantium::Flat
# Perform setup
# Perform strategy setup
#
# @return [self]
#
# @api private
#
def self.setup
self
def setup
end
# Perform teardown
# Perform strategy teardown
#
# @return [self]
#
# @api private
#
def self.teardown
self
def teardown
end
# Kill mutation
@ -34,18 +32,20 @@ module Mutant
#
# @api private
#
def self.kill(mutation)
def kill(mutation)
killer.new(self, mutation)
end
private
# Return killer
#
# @return [Class:Killer]
#
# @api private
#
def self.killer
self::KILLER
def killer
self.class::KILLER
end
end # Strategy

View file

@ -1,53 +0,0 @@
# encoding: utf-8
module Mutant
class Strategy
module MethodExpansion
# Run method name expansion
#
# @param [Symbol] name
#
# @return [Symbol]
#
# @api private
#
def self.run(name)
name = map(name) || expand(name)
end
# Return mapped name
#
# @param [Symbol] name
#
# @return [Symbol]
# if name was mapped
#
# @return [nil]
# otherwise
#
# @api private
#
def self.map(name)
OPERATOR_EXPANSIONS[name]
end
private_class_method :map
REGEXP = /#{Regexp.union(*METHOD_POSTFIX_EXPANSIONS.keys)}\z/.freeze
# Return expanded name
#
# @param [Symbol] name
#
# @return [Symbol]
#
# @api private
#
def self.expand(name)
name.to_s.gsub(REGEXP, METHOD_POSTFIX_EXPANSIONS).to_sym
end
private_class_method :expand
end # MethodExpansion
end # Strategy
end # Mutant

View file

@ -2,9 +2,9 @@
module Mutant
class Strategy
# Rspec strategy base class
# Rspec killer strategy
class Rspec < self
include Equalizer.new
KILLER = Killer::Forking.new(Killer::Rspec)
@ -14,52 +14,62 @@ module Mutant
#
# @api private
#
def self.setup
require('./spec/spec_helper.rb')
def setup
output = StringIO.new
configuration.error_stream = output
configuration.output_stream = output
options.configure(configuration)
configuration.load_spec_files
self
end
memoize :setup
# Run all unit specs per mutation
class Unit < self
# Return configuration
#
# @return [RSpec::Core::Configuration]
#
# @api private
#
def configuration
RSpec::Core::Configuration.new
end
memoize :configuration, :freezer => :noop
# Return file name pattern for mutation
#
# @return [Enumerable<String>]
#
# @api private
#
def self.spec_files(_mutation)
Dir['spec/unit/**/*_spec.rb']
end
end # Unit
# Return example groups
#
# @return [Enumerable<RSpec::Core::ExampleGroup>]
#
# @api private
#
def example_groups
world.example_groups
end
# Run all integration specs per mutation
class Integration < self
private
# Return file name pattern for mutation
#
# @return [Mutation]
#
# @api private
#
def self.spec_files(_mutation)
Dir['spec/integration/**/*_spec.rb']
end
end # Integration
# Return world
#
# @return [RSpec::Core::World]
#
# @api private
#
def world
RSpec.world
end
memoize :world, :freezer => :noop
# Run all specs per mutation
class Full < self
# Return spec files
#
# @return [Enumerable<String>]
#
# @api private
#
def self.spec_files(_mutation)
Dir['spec/**/*_spec.rb']
end
end # Full
# Return options
#
# @return [RSpec::Core::ConfigurationOptions]
#
# @api private
#
def options
options = RSpec::Core::ConfigurationOptions.new(%w(--fail-fast spec))
options.parse_options
options
end
memoize :options, :freezer => :noop
end # Rspec
end # Strategy

View file

@ -1,24 +0,0 @@
# encoding: utf-8
module Mutant
class Strategy
class Rspec
# DM2-style strategy
class DM2 < self
# Return filename pattern
#
# @param [Subject] subject
#
# @return [Enumerable<String>]
#
# @api private
#
def self.spec_files(subject)
Lookup.run(subject)
end
end # DM2
end # Rspec
end # Strategy
end # Mutant

View file

@ -1,63 +0,0 @@
# encoding: utf-8
module Mutant
class Strategy
class Rspec
class DM2
# Example lookup for the rspec dm2
class Lookup
include AbstractType, Adamantium::Flat, Concord::Public.new(:subject)
# Return glob expression
#
# @return [String]
#
# @api private
#
abstract_method :spec_files
# Perform example lookup
#
# @param [Subject] subject
#
# @return [Enumerable<String>]
#
# @api private
#
def self.run(subject)
build(subject).spec_files
end
REGISTRY = {}
# Register subject hander
#
# @param [Class:Subject]
#
# @return [undefined]
#
# @api private
#
def self.handle(subject_class)
REGISTRY[subject_class] = self
end
private_class_method :handle
# Build lookup object
#
# @param [Subject] subject
#
# @return [Lookup]
#
# @api private
#
def self.build(subject)
REGISTRY.fetch(subject.class).new(subject)
end
end # Lookup
end # DM2
end # Rspec
end # Strategy
end # Mutant

View file

@ -1,145 +0,0 @@
# encoding: utf-8
module Mutant
class Strategy
class Rspec
class DM2
class Lookup
# Base class for dm2 style method lookup
class Method < self
# Return spec files
#
# @return [Enumerable<String>]
#
# @api private
#
def spec_files
Dir.glob(glob_expression)
end
memoize :spec_files
private
# Return base path
#
# @return [String]
#
# @api private
#
def base_path
"spec/unit/#{Inflecto.underscore(subject.context.name)}"
end
# Return method name
#
# @return [Symbol]
#
# @api private
#
def method_name
subject.name
end
# Test if method is public
#
# @return [true]
# if method is public
#
# @return [false]
#
# @api private
#
def public?
subject.public?
end
# Return expanded name
#
# @return [String]
#
# @api private
#
def expanded_name
MethodExpansion.run(method_name)
end
# Return glob expression
#
# @return [String]
#
# @api private
#
def glob_expression
public? ? public_glob_expression : private_glob_expression
end
# Return public glob expression
#
# @return [String]
#
# @api private
#
def public_glob_expression
"#{base_path}/#{expanded_name}_spec.rb"
end
# Return private glob expression
#
# @return [String]
#
# @api private
#
def private_glob_expression
"#{base_path}/*_spec.rb"
end
# Instance method dm2 style method lookup
class Instance < self
handle(Subject::Method::Instance)
handle(Subject::Method::Instance::Memoized)
private
# Return glob expression
#
# @return [String]
#
# @api private
#
def glob_expression
glob_expression = super
if method_name == :initialize and !public?
"{#{glob_expression},#{base_path}/class_methods/new_spec.rb}"
else
glob_expression
end
end
end # Instance
# Singleton method dm2 style method lookup
class Singleton < self
handle(Subject::Method::Singleton)
private
# Return base path
#
# @return [String]
#
# @api private
#
def base_path
"#{super}/class_methods"
end
end # Singleton
end # Method
end # Lookup
end # DM2
end # Rspec
end # Strategy
end # Mutant

View file

@ -46,7 +46,7 @@ module Mutant
# @api private
#
def identification
"#{subtype}:#{source_path}:#{source_line}"
"#{match_expression}:#{source_path}:#{source_line}"
end
memoize :identification
@ -84,16 +84,26 @@ module Mutant
end
memoize :original_root
private
# Return subtype identifier
# Return match expression
#
# @return [String]
#
# @api private
#
abstract_method :subtype
private :subtype
abstract_method :match_expression
# Return match prefixes
#
# @return [Enumerable<String>]
#
# @api private
#
def match_prefixes
[match_expression].concat(context.match_prefixes)
end
memoize :match_prefixes
private
# Return neutral mutation
#

View file

@ -27,6 +27,16 @@ module Mutant
node.children[self.class::NAME_INDEX]
end
# Return match expression
#
# @return [String]
#
# @api private
#
def match_expression
"#{context.identification}#{self.class::SYMBOL}#{name}"
end
private
# Return mutations
@ -54,16 +64,6 @@ module Mutant
context.scope
end
# Return subtype identifier
#
# @return [String]
#
# @api private
#
def subtype
"#{context.identification}#{self.class::SYMBOL}#{name}"
end
end # Method
end # Subject
end # Mutant

View file

@ -1,29 +0,0 @@
# encoding: utf-8
require 'spec_helper'
describe Mutant, 'rspec integration' do
around do |example|
Dir.chdir(TestApp.root) do
example.run
end
end
let(:strategy) { Mutant::Strategy::Rspec::DM2 }
specify 'allows to kill mutations' do
cli = 'bundle exec mutant --rspec-dm2 ::TestApp::Literal#string'
Kernel.system(cli).should be(true)
end
specify 'fails to kill mutations when they are not covered' do
cli = 'bundle exec mutant --rspec-dm2 ::TestApp::Literal#uncovered_string'
Kernel.system(cli).should be(false)
end
specify 'fails when some mutations when are not covered' do
cli = 'bundle exec mutant --rspec-dm2 ::TestApp::Literal'
Kernel.system(cli).should be(false)
end
end

View file

@ -0,0 +1,26 @@
# encoding: utf-8
require 'spec_helper'
describe Mutant, 'rspec integration' do
around do |example|
Dir.chdir(TestApp.root) do
example.run
end
end
specify 'it allows to kill mutations' do
Kernel.system('bundle exec mutant --rspec ::TestApp::Literal#string').should be(true)
end
pending 'fails to kill mutations when they are not covered' do
cli = 'bundle exec mutant --rspec ::TestApp::Literal#uncovered_string'
Kernel.system(cli).should be(false)
end
pending 'fails when some mutations when are not covered' do
cli = 'bundle exec mutant --rspec ::TestApp::Literal'
Kernel.system(cli).should be(false)
end
end

View file

@ -29,7 +29,7 @@ describe Mutant::CLI, '.new' do
# Defaults
let(:expected_filter) { Mutant::Mutation::Filter::ALL }
let(:expected_strategy) { Mutant::Strategy::Rspec::Unit }
let(:expected_strategy) { Mutant::Strategy::Rspec.new }
let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
let(:ns) { Mutant::CLI::Classifier }
@ -56,9 +56,10 @@ describe Mutant::CLI, '.new' do
end
context 'with many strategy flags' do
let(:arguments) { %w(--rspec-unit --rspec-dm2) }
let(:arguments) { %w(--static-fail --rspec TestApp) }
let(:expected_matcher) { Mutant::CLI::Classifier::Namespace::Flat.new(Mutant::Cache.new, 'TestApp') }
let(:expected_strategy) { Mutant::Strategy::Rspec::DM2 }
it_should_behave_like 'a cli parser'
end
context 'without arguments' do
@ -70,30 +71,54 @@ describe Mutant::CLI, '.new' do
end
context 'with code filter and missing argument' do
let(:arguments) { %w(--rspec-unit --code) }
let(:arguments) { %w(--rspec --code) }
let(:expected_message) { 'missing argument: --code' }
it_should_behave_like 'an invalid cli run'
end
context 'with explicit method matcher' do
let(:arguments) { %w(--rspec-unit TestApp::Literal#float) }
let(:arguments) { %w(--rspec TestApp::Literal#float) }
let(:expected_matcher) { ns::Method.new(cache, 'TestApp::Literal#float') }
it_should_behave_like 'a cli parser'
end
context 'with debug flag' do
let(:matcher) { '::TestApp*' }
let(:arguments) { %W(--debug --rspec #{matcher}) }
let(:expected_matcher) { ns::Namespace::Recursive.new(cache, matcher) }
it_should_behave_like 'a cli parser'
it 'should set the debug option' do
subject.config.debug.should be(true)
end
end
context 'with zombie flag' do
let(:matcher) { '::TestApp*' }
let(:arguments) { %W(--zombie --rspec #{matcher}) }
let(:expected_matcher) { ns::Namespace::Recursive.new(cache, matcher) }
it_should_behave_like 'a cli parser'
it 'should set the zombie option' do
subject.config.zombie.should be(true)
end
end
context 'with namespace matcher' do
let(:matcher) { '::TestApp*' }
let(:arguments) { %W(--rspec-unit #{matcher}) }
let(:arguments) { %W(--rspec #{matcher}) }
let(:expected_matcher) { ns::Namespace::Recursive.new(cache, matcher) }
it_should_behave_like 'a cli parser'
end
context 'with code filter' do
let(:matcher) { 'TestApp::Literal#float' }
let(:arguments) { %W(--rspec-unit --code faa --code bbb #{matcher}) }
let(:matcher) { 'TestApp::Literal#float' }
let(:arguments) { %W(--rspec --code faa --code bbb #{matcher}) }
let(:filters) do
[

View file

@ -4,6 +4,10 @@ require 'spec_helper'
describe Mutant::Killer::Rspec, '.new' do
before do
pending 'dactivated'
end
subject { object.new(strategy, mutation) }
let(:context) { double('Context') }

View file

@ -1,51 +0,0 @@
# encoding: utf-8
require 'spec_helper'
describe Mutant::Strategy::MethodExpansion, '.run' do
subject { object.run(name) }
let(:object) { described_class }
context 'unexpandable and unmapped name' do
let(:name) { :foo }
it { should be(:foo) }
end
context 'expanded name' do
context 'predicate' do
let(:name) { :foo? }
it { should be(:foo_predicate) }
end
context 'writer' do
let(:name) { :foo= }
it { should be(:foo_writer) }
end
context 'bang' do
let(:name) { :foo! }
it { should be(:foo_bang) }
end
end
context 'operator expansions' do
Mutant::OPERATOR_EXPANSIONS.each do |name, expansion|
context "#{name}" do
let(:name) { name }
it "should expand to #{expansion}" do
should be(expansion)
end
end
end
end
end

View file

@ -1,73 +0,0 @@
# encoding: utf-8
require 'spec_helper'
singleton = Mutant::Strategy::Rspec::DM2::Lookup::Method::Instance
describe singleton, '#spec_files' do
subject { object.spec_files }
let(:object) { described_class.new(mutation_subject) }
let(:context) { double('Context', :name => 'Foo') }
let(:method_name) { :bar }
let(:files) { 'Files'.freeze }
let(:mutation_subject) do
double(
'Subject',
:name => method_name,
:public? => is_public,
:context => context
)
end
this_example_group = description
shared_examples_for this_example_group do
it_should_behave_like 'an idempotent method'
before do
if is_public
Mutant::Strategy::MethodExpansion
.should_receive(:run)
.with(method_name)
.and_return(:expanded_name)
end
Dir.should_receive(:glob)
.with(expected_glob_expression)
.and_return(files)
end
it { should be(files) }
it { should be_frozen }
end
context 'with public method' do
let(:is_public) { true }
let(:expected_glob_expression) { 'spec/unit/foo/expanded_name_spec.rb' }
it_should_behave_like this_example_group
end
context 'with nonpublic method' do
let(:is_public) { false }
context 'non initialize' do
let(:expected_glob_expression) { 'spec/unit/foo/*_spec.rb' }
it_should_behave_like this_example_group
end
context 'initialize' do
let(:method_name) { :initialize }
let(:expected_glob_expression) do
'{spec/unit/foo/*_spec.rb,spec/unit/foo/class_methods/new_spec.rb}'
end
it_should_behave_like this_example_group
end
end
end

View file

@ -1,62 +0,0 @@
# encoding: utf-8
require 'spec_helper'
singleton = Mutant::Strategy::Rspec::DM2::Lookup::Method::Singleton
describe singleton, '#spec_files' do
subject { object.spec_files }
let(:object) { described_class.new(mutation_subject) }
let(:method_name) { :bar }
let(:files) { 'Files'.freeze }
let(:context) { double('Context', :name => 'Foo') }
let(:mutation_subject) do
double(
'Subject',
:name => method_name,
:public? => is_public,
:context => context
)
end
this_example_group = description
shared_examples_for this_example_group do
it_should_behave_like 'an idempotent method'
before do
if is_public
Mutant::Strategy::MethodExpansion
.should_receive(:run)
.with(method_name)
.and_return(:expanded_name)
end
Dir.should_receive(:glob)
.with(expected_glob_expression)
.and_return(files)
end
it { should be(files) }
it { should be_frozen }
end
context 'with public method' do
let(:is_public) { true }
let(:expected_glob_expression) do
'spec/unit/foo/class_methods/expanded_name_spec.rb'
end
it_should_behave_like this_example_group
end
context 'with nonpublic method' do
let(:is_public) { false }
let(:expected_glob_expression) { 'spec/unit/foo/class_methods/*_spec.rb' }
it_should_behave_like this_example_group
end
end