mspec 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +9 -9
- data/lib/mspec.rb +5 -0
- data/lib/mspec/commands/mspec-ci.rb +1 -2
- data/lib/mspec/commands/mspec-run.rb +1 -2
- data/lib/mspec/commands/mspec-tag.rb +1 -2
- data/lib/mspec/helpers/const_lookup.rb +5 -1
- data/lib/mspec/matchers.rb +1 -0
- data/lib/mspec/matchers/match_yaml.rb +47 -0
- data/lib/mspec/mocks/mock.rb +56 -30
- data/lib/mspec/mocks/object.rb +3 -11
- data/lib/mspec/mocks/proxy.rb +24 -3
- data/lib/mspec/runner/mspec.rb +28 -15
- data/lib/mspec/utils/options.rb +0 -7
- data/lib/mspec/utils/script.rb +15 -5
- data/lib/mspec/version.rb +1 -1
- data/spec/commands/mspec_ci_spec.rb +3 -8
- data/spec/commands/mspec_run_spec.rb +3 -8
- data/spec/commands/mspec_tag_spec.rb +3 -8
- data/spec/helpers/const_lookup_spec.rb +29 -0
- data/spec/matchers/base_spec.rb +88 -52
- data/spec/matchers/eql_spec.rb +2 -2
- data/spec/matchers/equal_spec.rb +2 -2
- data/spec/matchers/equal_utf16_spec.rb +2 -2
- data/spec/matchers/match_yaml_spec.rb +39 -0
- data/spec/mocks/mock_spec.rb +191 -23
- data/spec/mocks/proxy_spec.rb +135 -28
- data/spec/runner/formatters/spinner_spec.rb +11 -4
- data/spec/runner/mspec_spec.rb +22 -23
- data/spec/spec_helper.rb +1 -6
- data/spec/utils/options_spec.rb +0 -21
- data/spec/utils/script_spec.rb +36 -11
- metadata +4 -2
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ task :default => :spec
|
|
9
9
|
|
10
10
|
spec = Gem::Specification.new do |s|
|
11
11
|
s.name = %q{mspec}
|
12
|
-
s.version = "1.
|
12
|
+
s.version = "1.1.0"
|
13
13
|
|
14
14
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
15
15
|
|
@@ -25,20 +25,20 @@ spec = Gem::Specification.new do |s|
|
|
25
25
|
s.rubyforge_project = 'http://rubyforge.org/projects/mspec'
|
26
26
|
s.require_paths = ["lib"]
|
27
27
|
s.rubygems_version = %q{1.1.1}
|
28
|
-
s.summary = <<EOS
|
29
|
-
MSpec is a specialized framework that is syntax-compatible
|
30
|
-
with RSpec for basic things like describe, it blocks and
|
28
|
+
s.summary = <<EOS
|
29
|
+
MSpec is a specialized framework that is syntax-compatible
|
30
|
+
with RSpec for basic things like describe, it blocks and
|
31
31
|
before, after actions.
|
32
32
|
|
33
|
-
MSpec contains additional features that assist in writing
|
34
|
-
the RubySpecs used by multiple Ruby implementations. Also,
|
35
|
-
MSpec attempts to use the simplest Ruby language features
|
33
|
+
MSpec contains additional features that assist in writing
|
34
|
+
the RubySpecs used by multiple Ruby implementations. Also,
|
35
|
+
MSpec attempts to use the simplest Ruby language features
|
36
36
|
so that beginning Ruby implementations can run it.
|
37
37
|
EOS
|
38
|
-
|
38
|
+
|
39
39
|
s.rdoc_options << '--title' << 'MSpec Gem' <<
|
40
40
|
'--main' << 'README' <<
|
41
41
|
'--line-numbers'
|
42
42
|
end
|
43
43
|
|
44
|
-
Rake::GemPackageTask.new(spec){ |pkg| pkg.gem_spec = spec }
|
44
|
+
Rake::GemPackageTask.new(spec){ |pkg| pkg.gem_spec = spec }
|
data/lib/mspec.rb
CHANGED
@@ -4,3 +4,8 @@ require 'mspec/mocks'
|
|
4
4
|
require 'mspec/runner'
|
5
5
|
require 'mspec/guards'
|
6
6
|
require 'mspec/helpers'
|
7
|
+
|
8
|
+
# If the implementation on which the specs are run cannot
|
9
|
+
# load pp from the standard library, add a pp.rb file that
|
10
|
+
# defines the #pretty_inspect method on Object or Kernel.
|
11
|
+
require 'pp'
|
@@ -20,7 +20,6 @@ class MSpecCI < MSpecScript
|
|
20
20
|
options.separator "\n How to run the specs"
|
21
21
|
options.add_config { |f| load f }
|
22
22
|
options.add_name
|
23
|
-
options.add_tags_dir
|
24
23
|
options.add_pretend
|
25
24
|
options.add_interrupt
|
26
25
|
|
@@ -59,7 +58,7 @@ class MSpecCI < MSpecScript
|
|
59
58
|
files.concat(Dir[item+"/**/*_spec.rb"].sort) if stat.directory?
|
60
59
|
end
|
61
60
|
|
62
|
-
MSpec.
|
61
|
+
MSpec.register_tags_patterns config[:tags_patterns]
|
63
62
|
MSpec.register_files files
|
64
63
|
TagFilter.new(:exclude, "fails").register
|
65
64
|
TagFilter.new(:exclude, "unstable").register
|
@@ -24,7 +24,6 @@ class MSpecRun < MSpecScript
|
|
24
24
|
options.separator "\n How to modify the execution"
|
25
25
|
options.add_config { |f| load f }
|
26
26
|
options.add_name
|
27
|
-
options.add_tags_dir
|
28
27
|
options.add_randomize
|
29
28
|
options.add_pretend
|
30
29
|
options.add_interrupt
|
@@ -70,7 +69,7 @@ class MSpecRun < MSpecScript
|
|
70
69
|
files.concat(Dir[item+"/**/*_spec.rb"].sort) if stat.directory?
|
71
70
|
end
|
72
71
|
|
73
|
-
MSpec.
|
72
|
+
MSpec.register_tags_patterns config[:tags_patterns]
|
74
73
|
MSpec.register_files files
|
75
74
|
|
76
75
|
MSpec.process
|
@@ -30,7 +30,6 @@ class MSpecTag < MSpecScript
|
|
30
30
|
options.separator "\n How to modify the execution"
|
31
31
|
options.add_config { |f| load f }
|
32
32
|
options.add_name
|
33
|
-
options.add_tags_dir
|
34
33
|
options.add_pretend
|
35
34
|
options.add_interrupt
|
36
35
|
|
@@ -77,7 +76,7 @@ class MSpecTag < MSpecScript
|
|
77
76
|
files.concat(Dir[item+"/**/*_spec.rb"].sort) if stat.directory?
|
78
77
|
end
|
79
78
|
|
80
|
-
MSpec.
|
79
|
+
MSpec.register_tags_patterns config[:tags_patterns]
|
81
80
|
MSpec.register_files files
|
82
81
|
|
83
82
|
MSpec.process
|
data/lib/mspec/matchers.rb
CHANGED
@@ -9,6 +9,7 @@ require 'mspec/matchers/be_true'
|
|
9
9
|
require 'mspec/matchers/equal'
|
10
10
|
require 'mspec/matchers/eql'
|
11
11
|
require 'mspec/matchers/include'
|
12
|
+
require 'mspec/matchers/match_yaml'
|
12
13
|
require 'mspec/matchers/raise_error'
|
13
14
|
require 'mspec/matchers/output'
|
14
15
|
require 'mspec/matchers/output_to_fd'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class MatchYAMLMatcher
|
2
|
+
|
3
|
+
def initialize(expected)
|
4
|
+
if valid_yaml?(expected)
|
5
|
+
@expected = expected
|
6
|
+
else
|
7
|
+
@expected = expected.to_yaml
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(actual)
|
12
|
+
@actual = actual
|
13
|
+
clean_yaml(@actual) == @expected
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
["Expected #{@actual.inspect}", " to match #{@expected.inspect}"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def negative_failure_message
|
21
|
+
["Expected #{@actual.inspect}", " to match #{@expected.inspect}"]
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def clean_yaml(yaml)
|
27
|
+
yaml.gsub(/([^-])\s+\n/, "\\1\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_yaml?(obj)
|
31
|
+
require 'yaml'
|
32
|
+
begin
|
33
|
+
YAML.load(obj)
|
34
|
+
rescue
|
35
|
+
false
|
36
|
+
else
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Object
|
43
|
+
def match_yaml(expected)
|
44
|
+
MatchYAMLMatcher.new(expected)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/lib/mspec/mocks/mock.rb
CHANGED
@@ -2,22 +2,35 @@ require 'mspec/expectations/expectations'
|
|
2
2
|
|
3
3
|
module Mock
|
4
4
|
def self.reset
|
5
|
-
@
|
5
|
+
@mocks = @stubs = nil
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.
|
9
|
-
@
|
8
|
+
def self.mocks
|
9
|
+
@mocks ||= Hash.new { |h,k| h[k] = [] }
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.
|
13
|
-
|
12
|
+
def self.stubs
|
13
|
+
@stubs ||= Hash.new { |h,k| h[k] = [] }
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.
|
16
|
+
def self.replaced_name(obj, sym)
|
17
|
+
:"__ms_#{obj.__id__}_#{sym}__"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.replaced_key(obj, sym)
|
21
|
+
[replaced_name(obj, sym), obj, sym]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.replaced?(key)
|
25
|
+
!!(mocks.keys + stubs.keys).find { |k| k.first == key.first }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.install_method(obj, sym, type=nil)
|
17
29
|
meta = class << obj; self; end
|
18
30
|
|
19
|
-
|
20
|
-
|
31
|
+
key = replaced_key obj, sym
|
32
|
+
if (sym.to_sym == :respond_to? or obj.respond_to?(sym)) and !replaced?(key)
|
33
|
+
meta.__send__ :alias_method, key.first, sym.to_sym
|
21
34
|
end
|
22
35
|
|
23
36
|
meta.class_eval <<-END
|
@@ -26,23 +39,26 @@ module Mock
|
|
26
39
|
end
|
27
40
|
END
|
28
41
|
|
29
|
-
|
42
|
+
proxy = MockProxy.new type
|
30
43
|
|
31
|
-
|
44
|
+
MSpec.actions :expectation, MSpec.current.state if proxy.mock?
|
32
45
|
|
33
|
-
if
|
34
|
-
|
35
|
-
proxy.at_least(0)
|
46
|
+
if proxy.stub?
|
47
|
+
stubs[key].unshift proxy
|
36
48
|
else
|
37
|
-
|
38
|
-
proxy.exactly(1)
|
49
|
+
mocks[key] << proxy
|
39
50
|
end
|
51
|
+
|
52
|
+
proxy
|
40
53
|
end
|
41
54
|
|
42
|
-
def self.
|
43
|
-
|
44
|
-
|
55
|
+
def self.name_or_inspect(obj)
|
56
|
+
obj.instance_variable_get(:@name) || obj.inspect
|
57
|
+
end
|
45
58
|
|
59
|
+
def self.verify_count
|
60
|
+
mocks.each do |key, proxies|
|
61
|
+
replaced, obj, sym = *key
|
46
62
|
proxies.each do |proxy|
|
47
63
|
qualifier, count = proxy.count
|
48
64
|
pass = case qualifier
|
@@ -52,12 +68,16 @@ module Mock
|
|
52
68
|
proxy.calls <= count
|
53
69
|
when :exactly
|
54
70
|
proxy.calls == count
|
71
|
+
when :any_number_of_times
|
72
|
+
true
|
55
73
|
else
|
56
74
|
false
|
57
75
|
end
|
58
76
|
unless pass
|
59
|
-
Expectation.fail_with(
|
60
|
-
|
77
|
+
Expectation.fail_with(
|
78
|
+
"Mock '#{name_or_inspect obj}' expected to receive '#{sym}' " \
|
79
|
+
"#{qualifier.to_s.sub('_', ' ')} #{count} times",
|
80
|
+
"but received it #{proxy.calls} times")
|
61
81
|
end
|
62
82
|
end
|
63
83
|
end
|
@@ -66,7 +86,9 @@ module Mock
|
|
66
86
|
def self.verify_call(obj, sym, *args, &block)
|
67
87
|
compare = *args
|
68
88
|
|
69
|
-
|
89
|
+
key = replaced_key obj, sym
|
90
|
+
proxies = mocks[key] + stubs[key]
|
91
|
+
proxies.each do |proxy|
|
70
92
|
pass = case proxy.arguments
|
71
93
|
when :any_args
|
72
94
|
true
|
@@ -75,20 +97,24 @@ module Mock
|
|
75
97
|
else
|
76
98
|
proxy.arguments == compare
|
77
99
|
end
|
78
|
-
|
100
|
+
|
79
101
|
if proxy.yielding?
|
80
102
|
if block
|
81
103
|
proxy.yielding.each do |args_to_yield|
|
82
104
|
if block.arity == -1 || block.arity == args_to_yield.size
|
83
105
|
block.call(*args_to_yield)
|
84
106
|
else
|
85
|
-
Expectation.fail_with(
|
86
|
-
|
107
|
+
Expectation.fail_with(
|
108
|
+
"Mock '#{name_or_inspect obj}' asked to yield " \
|
109
|
+
"|#{proxy.yielding.join(', ')}| on #{sym}\n",
|
110
|
+
"but a block with arity #{block.arity} was passed")
|
87
111
|
end
|
88
112
|
end
|
89
113
|
else
|
90
|
-
Expectation.fail_with(
|
91
|
-
|
114
|
+
Expectation.fail_with(
|
115
|
+
"Mock '#{name_or_inspect obj}' asked to yield " \
|
116
|
+
"|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
|
117
|
+
"but no block was passed")
|
92
118
|
end
|
93
119
|
end
|
94
120
|
|
@@ -99,18 +125,18 @@ module Mock
|
|
99
125
|
end
|
100
126
|
|
101
127
|
if sym.to_sym == :respond_to?
|
102
|
-
return obj.__send__(replaced_name(sym), compare)
|
128
|
+
return obj.__send__(replaced_name(obj, sym), compare)
|
103
129
|
else
|
104
|
-
Expectation.fail_with("Mock #{obj
|
130
|
+
Expectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
|
105
131
|
"called with unexpected arguments (#{Array(compare).join(' ')})")
|
106
132
|
end
|
107
133
|
end
|
108
134
|
|
109
135
|
def self.cleanup
|
110
|
-
|
136
|
+
symbols = mocks.keys + stubs.keys
|
137
|
+
symbols.uniq.each do |replaced, obj, sym|
|
111
138
|
meta = class << obj; self; end
|
112
139
|
|
113
|
-
replaced = replaced_name(sym)
|
114
140
|
if meta.instance_methods.include?(replaced.to_s)
|
115
141
|
meta.__send__ :alias_method, sym.to_sym, replaced
|
116
142
|
meta.__send__ :remove_method, replaced
|
data/lib/mspec/mocks/object.rb
CHANGED
@@ -1,16 +1,10 @@
|
|
1
1
|
require 'mspec/mocks/proxy'
|
2
2
|
|
3
|
-
class MockObject
|
4
|
-
def initialize(name)
|
5
|
-
@name = name
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
3
|
class Object
|
10
4
|
def stub!(sym)
|
11
5
|
Mock.install_method self, sym, :stub
|
12
6
|
end
|
13
|
-
|
7
|
+
|
14
8
|
def should_receive(sym)
|
15
9
|
Mock.install_method self, sym
|
16
10
|
end
|
@@ -18,11 +12,9 @@ class Object
|
|
18
12
|
def should_not_receive(sym)
|
19
13
|
proxy = Mock.install_method self, sym
|
20
14
|
proxy.exactly(0).times
|
21
|
-
# return nil so that further chained calls will raise
|
22
|
-
nil
|
23
15
|
end
|
24
16
|
|
25
|
-
def mock(name)
|
26
|
-
MockObject.new
|
17
|
+
def mock(name, options={})
|
18
|
+
MockObject.new name, options
|
27
19
|
end
|
28
20
|
end
|
data/lib/mspec/mocks/proxy.rb
CHANGED
@@ -1,13 +1,33 @@
|
|
1
|
+
class MockObject
|
2
|
+
def initialize(name, options={})
|
3
|
+
@name = name
|
4
|
+
@null = options[:null_object]
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(sym, *args, &block)
|
8
|
+
@null ? self : super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
1
12
|
class MockProxy
|
2
|
-
def initialize
|
13
|
+
def initialize(type=nil)
|
3
14
|
@multiple_returns = nil
|
4
15
|
@returning = nil
|
5
16
|
@yielding = []
|
6
17
|
@arguments = :any_args
|
18
|
+
@type = type || :mock
|
19
|
+
end
|
20
|
+
|
21
|
+
def mock?
|
22
|
+
@type == :mock
|
23
|
+
end
|
24
|
+
|
25
|
+
def stub?
|
26
|
+
@type == :stub
|
7
27
|
end
|
8
28
|
|
9
29
|
def count
|
10
|
-
@count ||= [:exactly, 0]
|
30
|
+
@count ||= mock? ? [:exactly, 1] : [:any_number_of_times, 0]
|
11
31
|
end
|
12
32
|
|
13
33
|
def arguments
|
@@ -61,7 +81,8 @@ class MockProxy
|
|
61
81
|
end
|
62
82
|
|
63
83
|
def any_number_of_times
|
64
|
-
|
84
|
+
@count = [:any_number_of_times, 0]
|
85
|
+
self
|
65
86
|
end
|
66
87
|
|
67
88
|
def with(*args)
|
data/lib/mspec/runner/mspec.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require 'mspec/runner/state'
|
3
2
|
require 'mspec/runner/tag'
|
4
3
|
require 'fileutils'
|
@@ -41,6 +40,9 @@ module MSpec
|
|
41
40
|
|
42
41
|
shuffle files if randomize?
|
43
42
|
files.each do |file|
|
43
|
+
@env = Object.new
|
44
|
+
@env.extend MSpec
|
45
|
+
|
44
46
|
store :file, file
|
45
47
|
actions :load
|
46
48
|
protect("loading #{file}") { Kernel.load file }
|
@@ -65,8 +67,15 @@ module MSpec
|
|
65
67
|
store :files, files
|
66
68
|
end
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
+
# Stores one or more substitution patterns for transforming
|
71
|
+
# a spec filename into a tags filename, where each pattern
|
72
|
+
# has the form:
|
73
|
+
#
|
74
|
+
# [Regexp, String]
|
75
|
+
#
|
76
|
+
# See also +tags_file+.
|
77
|
+
def self.register_tags_patterns(patterns)
|
78
|
+
store :tags_patterns, patterns
|
70
79
|
end
|
71
80
|
|
72
81
|
def self.register_mode(mode)
|
@@ -118,7 +127,7 @@ module MSpec
|
|
118
127
|
|
119
128
|
def self.protect(msg, &block)
|
120
129
|
begin
|
121
|
-
instance_eval(&block)
|
130
|
+
@env.instance_eval(&block)
|
122
131
|
rescue Exception => e
|
123
132
|
register_exit 1
|
124
133
|
if current and current.state
|
@@ -168,18 +177,22 @@ module MSpec
|
|
168
177
|
end
|
169
178
|
end
|
170
179
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
180
|
+
# Transforms a spec filename into a tags filename by applying each
|
181
|
+
# substitution pattern in :tags_pattern. The default patterns are:
|
182
|
+
#
|
183
|
+
# [%r(/spec/), '/spec/tags/'], [/_spec.rb$/, '_tags.txt']
|
184
|
+
#
|
185
|
+
# which will perform the following transformation:
|
186
|
+
#
|
187
|
+
# path/to/spec/class/method_spec.rb => path/to/spec/tags/class/method_tags.txt
|
188
|
+
#
|
189
|
+
# See also +register_tags_patterns+.
|
175
190
|
def self.tags_file
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
path = File.join(path, m[1]) if m
|
182
|
-
File.join path, tags_file
|
191
|
+
patterns = retrieve(:tags_patterns) ||
|
192
|
+
[[%r(spec/), 'spec/tags/'], [/_spec.rb$/, '_tags.txt']]
|
193
|
+
patterns.inject(retrieve(:file).dup) do |file, pattern|
|
194
|
+
file.gsub *pattern
|
195
|
+
end
|
183
196
|
end
|
184
197
|
|
185
198
|
def self.read_tags(*keys)
|