mspec 1.5.11 → 1.5.12
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 +1 -1
- data/lib/mspec/commands/mspec-ci.rb +1 -0
- data/lib/mspec/commands/mspec-run.rb +1 -0
- data/lib/mspec/commands/mspec-tag.rb +1 -0
- data/lib/mspec/helpers.rb +4 -0
- data/lib/mspec/helpers/ducktype.rb +33 -0
- data/lib/mspec/helpers/enumerator_class.rb +9 -0
- data/lib/mspec/helpers/environment.rb +10 -1
- data/lib/mspec/helpers/hash.rb +27 -0
- data/lib/mspec/helpers/io.rb +4 -0
- data/lib/mspec/helpers/metaclass.rb +7 -0
- data/lib/mspec/matchers.rb +5 -0
- data/lib/mspec/matchers/have_class_variable.rb +12 -0
- data/lib/mspec/matchers/have_constant.rb +6 -24
- data/lib/mspec/matchers/have_instance_variable.rb +12 -0
- data/lib/mspec/matchers/have_protected_instance_method.rb +24 -0
- data/lib/mspec/matchers/have_public_instance_method.rb +24 -0
- data/lib/mspec/matchers/stringsymboladapter.rb +1 -1
- data/lib/mspec/matchers/variable.rb +28 -0
- data/lib/mspec/mocks/mock.rb +70 -47
- data/lib/mspec/mocks/object.rb +4 -0
- data/lib/mspec/mocks/proxy.rb +17 -3
- data/lib/mspec/runner.rb +1 -0
- data/lib/mspec/runner/exception.rb +4 -3
- data/lib/mspec/utils/options.rb +10 -2
- data/lib/mspec/version.rb +1 -1
- data/spec/commands/mkspec_spec.rb +1 -1
- data/spec/commands/mspec_ci_spec.rb +5 -0
- data/spec/commands/mspec_run_spec.rb +5 -0
- data/spec/commands/mspec_spec.rb +2 -2
- data/spec/commands/mspec_tag_spec.rb +5 -0
- data/spec/expectations/should.rb +1 -0
- data/spec/helpers/ducktype_spec.rb +44 -0
- data/spec/helpers/enumerator_class_spec.rb +19 -0
- data/spec/helpers/hash_spec.rb +30 -0
- data/spec/helpers/io_spec.rb +5 -0
- data/spec/matchers/have_class_variable_spec.rb +75 -0
- data/spec/matchers/have_instance_variable_spec.rb +75 -0
- data/spec/matchers/have_protected_instance_method_spec.rb +57 -0
- data/spec/matchers/have_public_instance_method_spec.rb +53 -0
- data/spec/matchers/stringsymboladapter_spec.rb +8 -10
- data/spec/mocks/mock_spec.rb +5 -5
- data/spec/runner/exception_spec.rb +17 -10
- data/spec/utils/options_spec.rb +26 -0
- metadata +18 -2
data/Rakefile
CHANGED
@@ -18,7 +18,7 @@ spec = Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
20
20
|
s.authors = ["Brian Ford"]
|
21
|
-
s.date = %q{2009-
|
21
|
+
s.date = %q{2009-6-8}
|
22
22
|
s.email = %q{bford@engineyard.com}
|
23
23
|
s.has_rdoc = true
|
24
24
|
s.extra_rdoc_files = %w[ README LICENSE ]
|
data/lib/mspec/helpers.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'mspec/helpers/argv'
|
2
2
|
require 'mspec/helpers/bignum'
|
3
3
|
require 'mspec/helpers/const_lookup'
|
4
|
+
require 'mspec/helpers/ducktype'
|
5
|
+
require 'mspec/helpers/enumerator_class'
|
4
6
|
require 'mspec/helpers/environment'
|
5
7
|
require 'mspec/helpers/fixture'
|
6
8
|
require 'mspec/helpers/flunk'
|
9
|
+
require 'mspec/helpers/hash'
|
7
10
|
require 'mspec/helpers/io'
|
8
11
|
require 'mspec/helpers/language_version'
|
12
|
+
require 'mspec/helpers/metaclass'
|
9
13
|
require 'mspec/helpers/ruby_exe'
|
10
14
|
require 'mspec/helpers/scratch'
|
11
15
|
require 'mspec/helpers/tmp'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Object
|
2
|
+
def responds_to(sym)
|
3
|
+
metaclass.class_eval <<-END
|
4
|
+
def respond_to?(sym, include_private=false)
|
5
|
+
sym.to_sym == #{sym.to_sym.inspect} ? true : super
|
6
|
+
end
|
7
|
+
END
|
8
|
+
end
|
9
|
+
|
10
|
+
def does_not_respond_to(sym)
|
11
|
+
metaclass.class_eval <<-END
|
12
|
+
def respond_to?(sym, include_private=false)
|
13
|
+
sym.to_sym == #{sym.to_sym.inspect} ? false : super
|
14
|
+
end
|
15
|
+
END
|
16
|
+
end
|
17
|
+
|
18
|
+
def undefine(sym)
|
19
|
+
metaclass.class_eval <<-END
|
20
|
+
undef_method #{sym.to_sym.inspect}
|
21
|
+
END
|
22
|
+
end
|
23
|
+
|
24
|
+
def fake!(sym, value=nil)
|
25
|
+
responds_to sym
|
26
|
+
|
27
|
+
metaclass.class_eval <<-END
|
28
|
+
def method_missing(sym, *args)
|
29
|
+
return #{value.inspect} if sym.to_sym == #{sym.to_sym.inspect}
|
30
|
+
end
|
31
|
+
END
|
32
|
+
end
|
33
|
+
end
|
@@ -11,13 +11,22 @@ class Object
|
|
11
11
|
env
|
12
12
|
end
|
13
13
|
|
14
|
+
def windows_env_echo(var)
|
15
|
+
`cmd.exe /C ECHO %#{var}%`.strip
|
16
|
+
end
|
17
|
+
|
14
18
|
def username
|
15
19
|
user = ""
|
16
20
|
if SpecGuard.windows?
|
17
|
-
user =
|
21
|
+
user = windows_env_echo('USERNAME')
|
18
22
|
else
|
19
23
|
user = `whoami`.strip
|
20
24
|
end
|
21
25
|
user
|
22
26
|
end
|
27
|
+
|
28
|
+
def home_directory
|
29
|
+
return ENV['HOME'] unless SpecGuard.windows?
|
30
|
+
windows_env_echo('HOMEDRIVE') + windows_env_echo('HOMEPATH')
|
31
|
+
end
|
23
32
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Object
|
2
|
+
# The following helpers provide a level of indirection for running the specs
|
3
|
+
# against a Hash implementation that has a different name than Hash.
|
4
|
+
|
5
|
+
# Returns the Hash class.
|
6
|
+
unless method_defined?(:hash_class)
|
7
|
+
def hash_class
|
8
|
+
Hash
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a new instance of hash_class.
|
13
|
+
def new_hash(*args, &block)
|
14
|
+
if block
|
15
|
+
hash_class.new(&block)
|
16
|
+
elsif args.size == 1
|
17
|
+
value = args.first
|
18
|
+
if value.is_a?(Hash) or value.is_a?(hash_class)
|
19
|
+
hash_class[value]
|
20
|
+
else
|
21
|
+
hash_class.new value
|
22
|
+
end
|
23
|
+
else
|
24
|
+
hash_class[*args]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/mspec/helpers/io.rb
CHANGED
data/lib/mspec/matchers.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mspec/matchers/base'
|
2
|
+
require 'mspec/matchers/be_an_instance_of'
|
2
3
|
require 'mspec/matchers/be_ancestor_of'
|
3
4
|
require 'mspec/matchers/be_close'
|
4
5
|
require 'mspec/matchers/be_empty'
|
@@ -12,9 +13,13 @@ require 'mspec/matchers/equal'
|
|
12
13
|
require 'mspec/matchers/equal_element'
|
13
14
|
require 'mspec/matchers/equal_utf16'
|
14
15
|
require 'mspec/matchers/have_constant'
|
16
|
+
require 'mspec/matchers/have_class_variable'
|
15
17
|
require 'mspec/matchers/have_instance_method'
|
18
|
+
require 'mspec/matchers/have_instance_variable'
|
16
19
|
require 'mspec/matchers/have_method'
|
17
20
|
require 'mspec/matchers/have_private_instance_method'
|
21
|
+
require 'mspec/matchers/have_protected_instance_method'
|
22
|
+
require 'mspec/matchers/have_public_instance_method'
|
18
23
|
require 'mspec/matchers/include'
|
19
24
|
require 'mspec/matchers/match_yaml'
|
20
25
|
require 'mspec/matchers/raise_error'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mspec/matchers/variable'
|
2
|
+
|
3
|
+
class HaveClassVariableMatcher < VariableMatcher
|
4
|
+
self.variables_method = :class_variables
|
5
|
+
self.description = 'class variable'
|
6
|
+
end
|
7
|
+
|
8
|
+
class Object
|
9
|
+
def have_class_variable(variable)
|
10
|
+
HaveClassVariableMatcher.new(variable)
|
11
|
+
end
|
12
|
+
end
|
@@ -1,30 +1,12 @@
|
|
1
|
-
require 'mspec/matchers/
|
1
|
+
require 'mspec/matchers/variable'
|
2
2
|
|
3
|
-
class HaveConstantMatcher
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(name)
|
7
|
-
@name = convert_name name
|
8
|
-
end
|
9
|
-
|
10
|
-
def matches?(mod)
|
11
|
-
@mod = mod
|
12
|
-
@mod.constants.include? @name
|
13
|
-
end
|
14
|
-
|
15
|
-
def failure_message
|
16
|
-
["Expected #{@mod} to have constant '#{@name.to_s}'",
|
17
|
-
"but it does not"]
|
18
|
-
end
|
19
|
-
|
20
|
-
def negative_failure_message
|
21
|
-
["Expected #{@mod} NOT to have constant '#{@name.to_s}'",
|
22
|
-
"but it does"]
|
23
|
-
end
|
3
|
+
class HaveConstantMatcher < VariableMatcher
|
4
|
+
self.variables_method = :constants
|
5
|
+
self.description = 'constant'
|
24
6
|
end
|
25
7
|
|
26
8
|
class Object
|
27
|
-
def have_constant(
|
28
|
-
HaveConstantMatcher.new
|
9
|
+
def have_constant(variable)
|
10
|
+
HaveConstantMatcher.new(variable)
|
29
11
|
end
|
30
12
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mspec/matchers/variable'
|
2
|
+
|
3
|
+
class HaveInstanceVariableMatcher < VariableMatcher
|
4
|
+
self.variables_method = :instance_variables
|
5
|
+
self.description = 'instance variable'
|
6
|
+
end
|
7
|
+
|
8
|
+
class Object
|
9
|
+
def have_instance_variable(variable)
|
10
|
+
HaveInstanceVariableMatcher.new(variable)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mspec/matchers/method'
|
2
|
+
|
3
|
+
class HaveProtectedInstanceMethodMatcher < MethodMatcher
|
4
|
+
def matches?(mod)
|
5
|
+
@mod = mod
|
6
|
+
mod.protected_instance_methods(@include_super).include? @method
|
7
|
+
end
|
8
|
+
|
9
|
+
def failure_message
|
10
|
+
["Expected #{@mod} to have protected instance method '#{@method.to_s}'",
|
11
|
+
"but it does not"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def negative_failure_message
|
15
|
+
["Expected #{@mod} NOT to have protected instance method '#{@method.to_s}'",
|
16
|
+
"but it does"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Object
|
21
|
+
def have_protected_instance_method(method, include_super=true)
|
22
|
+
HaveProtectedInstanceMethodMatcher.new method, include_super
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mspec/matchers/method'
|
2
|
+
|
3
|
+
class HavePublicInstanceMethodMatcher < MethodMatcher
|
4
|
+
def matches?(mod)
|
5
|
+
@mod = mod
|
6
|
+
mod.public_instance_methods(@include_super).include? @method
|
7
|
+
end
|
8
|
+
|
9
|
+
def failure_message
|
10
|
+
["Expected #{@mod} to have public instance method '#{@method.to_s}'",
|
11
|
+
"but it does not"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def negative_failure_message
|
15
|
+
["Expected #{@mod} NOT to have public instance method '#{@method.to_s}'",
|
16
|
+
"but it does"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Object
|
21
|
+
def have_public_instance_method(method, include_super=true)
|
22
|
+
HavePublicInstanceMethodMatcher.new method, include_super
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mspec/matchers/stringsymboladapter'
|
2
|
+
|
3
|
+
class VariableMatcher
|
4
|
+
include StringSymbolAdapter
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :variables_method, :description
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(variable)
|
11
|
+
@variable = convert_name(variable)
|
12
|
+
end
|
13
|
+
|
14
|
+
def matches?(object)
|
15
|
+
@object = object
|
16
|
+
@object.send(self.class.variables_method).include? @variable
|
17
|
+
end
|
18
|
+
|
19
|
+
def failure_message
|
20
|
+
["Expected #{@object} to have #{self.class.description} '#{@variable}'",
|
21
|
+
"but it does not"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def negative_failure_message
|
25
|
+
["Expected #{@object} NOT to have #{self.class.description} '#{@variable}'",
|
26
|
+
"but it does"]
|
27
|
+
end
|
28
|
+
end
|
data/lib/mspec/mocks/mock.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
require 'mspec/expectations/expectations'
|
2
|
+
require 'mspec/helpers/metaclass'
|
2
3
|
|
3
4
|
module Mock
|
4
5
|
def self.reset
|
5
|
-
@mocks = @stubs = nil
|
6
|
+
@mocks = @stubs = @objects = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.objects
|
10
|
+
@objects ||= {}
|
6
11
|
end
|
7
12
|
|
8
13
|
def self.mocks
|
@@ -14,23 +19,38 @@ module Mock
|
|
14
19
|
end
|
15
20
|
|
16
21
|
def self.replaced_name(obj, sym)
|
17
|
-
:"
|
22
|
+
:"__mspec_#{obj.__id__}_#{sym}__"
|
18
23
|
end
|
19
24
|
|
20
25
|
def self.replaced_key(obj, sym)
|
21
|
-
[replaced_name(obj, sym),
|
26
|
+
[replaced_name(obj, sym), sym]
|
22
27
|
end
|
23
28
|
|
24
|
-
def self.
|
25
|
-
!!
|
29
|
+
def self.has_key?(keys, sym)
|
30
|
+
!!keys.find { |k| k.first == sym }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.replaced?(sym)
|
34
|
+
has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.mock_respond_to?(obj, sym)
|
38
|
+
name = replaced_name(obj, :respond_to?)
|
39
|
+
if replaced? name
|
40
|
+
obj.__send__ name, sym
|
41
|
+
else
|
42
|
+
obj.respond_to? sym
|
43
|
+
end
|
26
44
|
end
|
27
45
|
|
28
46
|
def self.install_method(obj, sym, type=nil)
|
29
|
-
meta =
|
47
|
+
meta = obj.metaclass
|
30
48
|
|
31
49
|
key = replaced_key obj, sym
|
32
|
-
|
33
|
-
|
50
|
+
sym = sym.to_sym
|
51
|
+
|
52
|
+
if (sym == :respond_to? or mock_respond_to?(obj, sym)) and !replaced?(key.first)
|
53
|
+
meta.__send__ :alias_method, key.first, sym
|
34
54
|
end
|
35
55
|
|
36
56
|
meta.class_eval <<-END
|
@@ -51,6 +71,7 @@ module Mock
|
|
51
71
|
else
|
52
72
|
mocks[key] << proxy
|
53
73
|
end
|
74
|
+
objects[key] = obj
|
54
75
|
|
55
76
|
proxy
|
56
77
|
end
|
@@ -61,7 +82,7 @@ module Mock
|
|
61
82
|
|
62
83
|
def self.verify_count
|
63
84
|
mocks.each do |key, proxies|
|
64
|
-
|
85
|
+
obj = objects[key]
|
65
86
|
proxies.each do |proxy|
|
66
87
|
qualifier, count = proxy.count
|
67
88
|
pass = case qualifier
|
@@ -78,7 +99,7 @@ module Mock
|
|
78
99
|
end
|
79
100
|
unless pass
|
80
101
|
SpecExpectation.fail_with(
|
81
|
-
"Mock '#{name_or_inspect obj}' expected to receive '#{
|
102
|
+
"Mock '#{name_or_inspect obj}' expected to receive '#{key.last}' " \
|
82
103
|
"#{qualifier.to_s.sub('_', ' ')} #{count} times",
|
83
104
|
"but received it #{proxy.calls} times")
|
84
105
|
end
|
@@ -88,50 +109,51 @@ module Mock
|
|
88
109
|
|
89
110
|
def self.verify_call(obj, sym, *args, &block)
|
90
111
|
compare = *args
|
91
|
-
if
|
112
|
+
if (behaves_like_ruby_1_9 = *[])
|
92
113
|
compare = compare.first if compare.length <= 1
|
93
114
|
end
|
94
115
|
|
95
116
|
key = replaced_key obj, sym
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
117
|
+
[mocks, stubs].each do |proxies|
|
118
|
+
proxies[key].each do |proxy|
|
119
|
+
pass = case proxy.arguments
|
120
|
+
when :any_args
|
121
|
+
true
|
122
|
+
when :no_args
|
123
|
+
compare.nil?
|
124
|
+
else
|
125
|
+
proxy.arguments == compare
|
126
|
+
end
|
106
127
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
128
|
+
if proxy.yielding?
|
129
|
+
if block
|
130
|
+
proxy.yielding.each do |args_to_yield|
|
131
|
+
if block.arity == -1 || block.arity == args_to_yield.size
|
132
|
+
block.call(*args_to_yield)
|
133
|
+
else
|
134
|
+
SpecExpectation.fail_with(
|
135
|
+
"Mock '#{name_or_inspect obj}' asked to yield " \
|
136
|
+
"|#{proxy.yielding.join(', ')}| on #{sym}\n",
|
137
|
+
"but a block with arity #{block.arity} was passed")
|
138
|
+
end
|
117
139
|
end
|
140
|
+
else
|
141
|
+
SpecExpectation.fail_with(
|
142
|
+
"Mock '#{name_or_inspect obj}' asked to yield " \
|
143
|
+
"|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
|
144
|
+
"but no block was passed")
|
118
145
|
end
|
119
|
-
else
|
120
|
-
SpecExpectation.fail_with(
|
121
|
-
"Mock '#{name_or_inspect obj}' asked to yield " \
|
122
|
-
"|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
|
123
|
-
"but no block was passed")
|
124
146
|
end
|
125
|
-
end
|
126
147
|
|
127
|
-
|
128
|
-
|
129
|
-
|
148
|
+
if pass
|
149
|
+
proxy.called
|
150
|
+
return proxy.returning
|
151
|
+
end
|
130
152
|
end
|
131
153
|
end
|
132
154
|
|
133
155
|
if sym.to_sym == :respond_to?
|
134
|
-
|
156
|
+
mock_respond_to? obj, compare
|
135
157
|
else
|
136
158
|
SpecExpectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
|
137
159
|
"called with unexpected arguments (#{Array(compare).join(' ')})")
|
@@ -139,15 +161,16 @@ module Mock
|
|
139
161
|
end
|
140
162
|
|
141
163
|
def self.cleanup
|
142
|
-
|
143
|
-
|
144
|
-
|
164
|
+
objects.each do |key, obj|
|
165
|
+
replaced = key.first
|
166
|
+
sym = key.last
|
167
|
+
meta = obj.metaclass
|
145
168
|
|
146
|
-
if meta.instance_methods.map { |x| x.to_sym }.include?(replaced
|
147
|
-
meta.__send__ :alias_method, sym
|
169
|
+
if meta.instance_methods.map { |x| x.to_sym }.include?(replaced)
|
170
|
+
meta.__send__ :alias_method, sym, replaced
|
148
171
|
meta.__send__ :remove_method, replaced
|
149
172
|
else
|
150
|
-
meta.__send__ :remove_method, sym
|
173
|
+
meta.__send__ :remove_method, sym
|
151
174
|
end
|
152
175
|
end
|
153
176
|
reset
|