mockery 0.4.1
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 +106 -0
- data/lib/mockery/call.rb +40 -0
- data/lib/mockery/controller.rb +75 -0
- data/lib/mockery/mock.rb +158 -0
- data/lib/mockery/mock_class_factory.rb +54 -0
- data/lib/mockery/record_control.rb +54 -0
- data/lib/mockery/recorder.rb +48 -0
- data/tests/acc/acc_tests.rb +18 -0
- data/tests/acc/mockery/mockery_test.rb +105 -0
- data/tests/unit/mockery/call_test.rb +92 -0
- data/tests/unit/mockery/controller_test.rb +207 -0
- data/tests/unit/mockery/mock_class_factory_test.rb +207 -0
- data/tests/unit/mockery/mock_test.rb +247 -0
- data/tests/unit/mockery/record_control_test.rb +163 -0
- data/tests/unit/mockery/recorder_test.rb +67 -0
- data/tests/unit/unit_tests.rb +29 -0
- metadata +57 -0
data/Rakefile
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/packagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
PKG_VERSION = "0.4.1"
|
9
|
+
|
10
|
+
desc "Default task"
|
11
|
+
task :default => [:unit_tests, :acc_tests]
|
12
|
+
|
13
|
+
desc "Run the unit tests"
|
14
|
+
Rake::TestTask.new do |t|
|
15
|
+
t.name = 'unit_tests'
|
16
|
+
#t.libs << 'tests/unit'
|
17
|
+
#t.libs << 'lib'
|
18
|
+
t.test_files = FileList['tests/unit/unit_tests.rb']
|
19
|
+
t.verbose = true
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run the acceptance tests"
|
23
|
+
Rake::TestTask.new do |t|
|
24
|
+
t.name = 'acc_tests'
|
25
|
+
#t.libs << 'tests/acc'
|
26
|
+
#t.libs << 'lib'
|
27
|
+
t.test_files = FileList['tests/acc/acc_tests.rb']
|
28
|
+
t.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
SOURCE_FILES = FileList.new do |fl|
|
32
|
+
["lib", "tests"].each do |dir|
|
33
|
+
fl.include "#{dir}/**/*"
|
34
|
+
end
|
35
|
+
fl.include "Rakefile"
|
36
|
+
fl.include "setup.rb"
|
37
|
+
fl.exclude( /\bCVS\b/ )
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Build the Mockery distribution"
|
41
|
+
Rake::PackageTask.new("mockery", PKG_VERSION) do |p|
|
42
|
+
p.need_tar = true
|
43
|
+
p.need_zip = true
|
44
|
+
p.package_dir = '../../tmp/mockery-pkg'
|
45
|
+
p.package_files = SOURCE_FILES
|
46
|
+
end
|
47
|
+
|
48
|
+
Rake::RDocTask.new do |rd|
|
49
|
+
rd.rdoc_files.include('lib/**/*.rb')
|
50
|
+
rd.template = 'jamis'
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Create the 'build' directory"
|
54
|
+
task :build_build do
|
55
|
+
Dir.mkdir('build') unless File.directory?('build')
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Build rdoc pages"
|
59
|
+
task :build_rdoc => [:rdoc, :build_build] do
|
60
|
+
File.rename('html', 'build/rdoc')
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "create missing file rdoc blank.html"
|
64
|
+
task :build_blank do
|
65
|
+
if ! File.exist?("build/rdoc/blank.html")
|
66
|
+
File.open('build/rdoc/blank.html', 'w') do |handle|
|
67
|
+
handle.puts('<html/>')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
GEM_FILES = FileList.new do |fl|
|
73
|
+
['lib', 'tests'].each do |dir|
|
74
|
+
fl.include "#{dir}/**/*"
|
75
|
+
end
|
76
|
+
fl.include "Rakefile"
|
77
|
+
fl.exclude( /\bCVS\b/ )
|
78
|
+
end
|
79
|
+
|
80
|
+
gem_spec = Gem::Specification.new do |s|
|
81
|
+
s.autorequire = 'mockery/controller'
|
82
|
+
s.description = <<EOF
|
83
|
+
Mockery dynamically generates mock objects and verifies
|
84
|
+
that they are used as expected.
|
85
|
+
EOF
|
86
|
+
s.email = 'shea@gtsdesign.com'
|
87
|
+
s.files = GEM_FILES
|
88
|
+
s.has_rdoc = false
|
89
|
+
s.homepage = 'http://mockery.rubyforge.org'
|
90
|
+
s.name = 'mockery'
|
91
|
+
s.requirements << 'none'
|
92
|
+
s.rubyforge_project = 'mockery'
|
93
|
+
s.summary = "Dynamic mock objects"
|
94
|
+
s.version = PKG_VERSION
|
95
|
+
end
|
96
|
+
|
97
|
+
desc "Create a .gem file for Mockery"
|
98
|
+
task :gem do
|
99
|
+
end
|
100
|
+
|
101
|
+
Rake::GemPackageTask.new(gem_spec) do |p|
|
102
|
+
p.package_dir = '../../tmp/mockery-pkg'
|
103
|
+
p.need_zip = true
|
104
|
+
p.need_tar = true
|
105
|
+
end
|
106
|
+
|
data/lib/mockery/call.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module Mockery
|
3
|
+
|
4
|
+
class Call
|
5
|
+
|
6
|
+
attr_accessor :args,
|
7
|
+
:caller,
|
8
|
+
:method_name,
|
9
|
+
:return_value
|
10
|
+
|
11
|
+
def digest(caller, args)
|
12
|
+
@caller = caller
|
13
|
+
@method_name = args[0]
|
14
|
+
@args = args[1 .. -1]
|
15
|
+
end
|
16
|
+
|
17
|
+
# equality of Call objects is based on equality of
|
18
|
+
# the attributes, with the exception of @caller (since
|
19
|
+
# the recording is done by a Recorder, not an object
|
20
|
+
# of the target class)
|
21
|
+
def ==(call)
|
22
|
+
return call != nil &&
|
23
|
+
call.class == Mockery::Call &&
|
24
|
+
@method_name == call.method_name &&
|
25
|
+
@args == call.args
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
args_str = (@args == nil) ?
|
30
|
+
'nil' :
|
31
|
+
"[#{@args.join(',')}]"
|
32
|
+
return "<#{self.class.name}:0x#{object_id.abs.to_s(16)}" +
|
33
|
+
" caller=#{@caller}" +
|
34
|
+
" method_name=#{@method_name}" +
|
35
|
+
" args=#{args_str}" +
|
36
|
+
">"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Name: Mockery::Controller (lib/mockery/controller.rb)
|
4
|
+
#
|
5
|
+
# Description:
|
6
|
+
#
|
7
|
+
# The Controller class coordinates a test.
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# Copyright 2005 Gary Shea
|
11
|
+
#
|
12
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
13
|
+
# you may not use this file except in compliance with the License.
|
14
|
+
# You may obtain a copy of the License at
|
15
|
+
#
|
16
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
#
|
18
|
+
# Unless required by applicable law or agreed to in writing, software
|
19
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
20
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
21
|
+
# See the License for the specific language governing permissions and
|
22
|
+
# limitations under the License.
|
23
|
+
#
|
24
|
+
|
25
|
+
require 'mockery/call'
|
26
|
+
require 'mockery/mock'
|
27
|
+
require 'mockery/mock_class_factory'
|
28
|
+
require 'mockery/record_control'
|
29
|
+
require 'mockery/recorder'
|
30
|
+
|
31
|
+
module Mockery
|
32
|
+
|
33
|
+
class Controller
|
34
|
+
|
35
|
+
def initialize(*klasses)
|
36
|
+
@mocks = []
|
37
|
+
klasses.each do |klass|
|
38
|
+
mock = Mock.new(klass)
|
39
|
+
@mocks << mock
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def record(&block)
|
44
|
+
recorders = @mocks.collect { |mock| mock.recorder }
|
45
|
+
ctl = RecordControl.new(recorders)
|
46
|
+
args = [recorders, ctl].flatten[0 ... block.arity.abs]
|
47
|
+
block.call(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def try(&block)
|
51
|
+
objs = @mocks.collect { |mock| mock.mocked_object }
|
52
|
+
block.call(*objs)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate
|
56
|
+
@mocks.each do |mock|
|
57
|
+
return false if ! mock.validate
|
58
|
+
end
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
|
62
|
+
def error_report
|
63
|
+
report = ''
|
64
|
+
index = 1
|
65
|
+
@mocks.each do |mock|
|
66
|
+
tmp_index = index
|
67
|
+
index = index + 1
|
68
|
+
report << "mock #{tmp_index} (class: #{mock.class})\n"
|
69
|
+
report << mock.error_report
|
70
|
+
end
|
71
|
+
return report
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
data/lib/mockery/mock.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Name: Mockery::Mock (lib/mockery/mock.rb)
|
4
|
+
#
|
5
|
+
# Description:
|
6
|
+
#
|
7
|
+
# The Mock class handles configuration, construction,
|
8
|
+
# and validation of a single mock object.
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# Copyright 2005 Gary Shea
|
12
|
+
#
|
13
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
14
|
+
# you may not use this file except in compliance with the License.
|
15
|
+
# You may obtain a copy of the License at
|
16
|
+
#
|
17
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
18
|
+
#
|
19
|
+
# Unless required by applicable law or agreed to in writing, software
|
20
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
21
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
22
|
+
# See the License for the specific language governing permissions and
|
23
|
+
# limitations under the License.
|
24
|
+
#
|
25
|
+
|
26
|
+
require 'mockery/call'
|
27
|
+
require 'mockery/mock_class_factory'
|
28
|
+
require 'mockery/recorder'
|
29
|
+
|
30
|
+
module Mockery
|
31
|
+
|
32
|
+
class Mock
|
33
|
+
|
34
|
+
attr_reader :klass, :recorder
|
35
|
+
|
36
|
+
def initialize(klass)
|
37
|
+
@klass = klass
|
38
|
+
@recorder = Recorder.new(klass)
|
39
|
+
@class_factory = MockClassFactory.new(klass)
|
40
|
+
@return_values = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_history
|
44
|
+
@recorder.history.each do |call|
|
45
|
+
method = call.method_name
|
46
|
+
if @return_values.has_key?(method)
|
47
|
+
@return_values[method] << call.return_value
|
48
|
+
else
|
49
|
+
@return_values[method] = [call.return_value]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_methods
|
55
|
+
@return_values.each_key do |method_name|
|
56
|
+
@class_factory.add_method(method_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def mocked_object
|
61
|
+
@return_values = Hash.new
|
62
|
+
process_history
|
63
|
+
add_methods
|
64
|
+
@class_factory.create_initialize(@return_values.keys)
|
65
|
+
@instance = @class_factory.create_instance(@return_values)
|
66
|
+
return @instance
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate
|
70
|
+
return @recorder.history == @instance.instance_history
|
71
|
+
end
|
72
|
+
|
73
|
+
def error_report
|
74
|
+
|
75
|
+
report = ''
|
76
|
+
|
77
|
+
recorded = @recorder.history
|
78
|
+
actual = @instance.instance_history
|
79
|
+
if recorded.size > actual.size
|
80
|
+
max_idx = @recorder.history.size
|
81
|
+
else
|
82
|
+
max_idx = @instance.instance_history.size
|
83
|
+
end
|
84
|
+
|
85
|
+
(0 ... max_idx).each do |idx|
|
86
|
+
if idx >= recorded.size
|
87
|
+
report << "unexpected: #{describe_call(actual[idx])}\n"
|
88
|
+
elsif idx >= actual.size
|
89
|
+
report << "expected: #{describe_call(recorded[idx])}\n"
|
90
|
+
elsif recorded[idx] != actual[idx]
|
91
|
+
report << "expected\n#{describe_call(recorded[idx])}\n"
|
92
|
+
report << "but got\n#{describe_call(actual[idx])}\n"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
return report
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def describe_call(call)
|
101
|
+
return '<' + call.caller.class.name + '>.' +
|
102
|
+
call.method_name.to_s + '(' +
|
103
|
+
call.args[0 .. -1].collect{|arg| expanding_describe_value(arg)}.join(', ') +
|
104
|
+
')'
|
105
|
+
end
|
106
|
+
|
107
|
+
def expanding_describe_value(value)
|
108
|
+
dscr = describe_simple_value(value)
|
109
|
+
if dscr == nil
|
110
|
+
dscr = "<#{value.class.name}:#{object_id_to_s(value)} "
|
111
|
+
dscr << value.instance_variables.collect { |var_name|
|
112
|
+
var_dscr = ''
|
113
|
+
var_dscr << "#{var_name}="
|
114
|
+
var_value = value.instance_variable_get(eval(":#{var_name}"))
|
115
|
+
var_dscr << non_expanding_describe_value(var_value)
|
116
|
+
var_dscr
|
117
|
+
}.join(', ')
|
118
|
+
dscr << '>'
|
119
|
+
end
|
120
|
+
return dscr
|
121
|
+
end
|
122
|
+
|
123
|
+
def non_expanding_describe_value(value)
|
124
|
+
dscr = describe_simple_value(value)
|
125
|
+
if dscr == nil
|
126
|
+
dscr = '<' + value.class.name + object_id_to_s(value) + '>'
|
127
|
+
end
|
128
|
+
return dscr
|
129
|
+
end
|
130
|
+
|
131
|
+
def describe_simple_value(value)
|
132
|
+
if value == nil
|
133
|
+
return 'nil'
|
134
|
+
elsif value.class == Fixnum
|
135
|
+
return value.to_s
|
136
|
+
elsif value.class == Symbol
|
137
|
+
return ':' + value.to_s
|
138
|
+
elsif value.class == String
|
139
|
+
return "'" + value + "'"
|
140
|
+
elsif value.class == Array
|
141
|
+
return '[' + value.join(', ') + ']'
|
142
|
+
elsif value.class == Hash
|
143
|
+
dscrs = []
|
144
|
+
value.each { |key, value|
|
145
|
+
dscrs << "#{key} => #{value}"
|
146
|
+
}
|
147
|
+
return '{' + dscrs.join(',') + '}'
|
148
|
+
else
|
149
|
+
return nil
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def object_id_to_s(obj)
|
154
|
+
return ':0x' + obj.object_id.abs.to_s(16)
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
module Mockery
|
3
|
+
|
4
|
+
class MockClassFactory
|
5
|
+
|
6
|
+
def initialize(user_klass)
|
7
|
+
|
8
|
+
klass = Class.new(user_klass)
|
9
|
+
klass.class_eval {
|
10
|
+
attr_reader :instance_history
|
11
|
+
}
|
12
|
+
|
13
|
+
@klass = klass
|
14
|
+
@return_values = Hash.new
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_method(method_name)
|
19
|
+
var_name = "@___#{method_name}_rv"
|
20
|
+
def_string = "@klass.class_eval {\n" +
|
21
|
+
" def #{method_name}(*args)\n" +
|
22
|
+
" call = Mockery::Call.new\n" +
|
23
|
+
" call.caller = self\n" +
|
24
|
+
" call.method_name = :#{method_name}\n" +
|
25
|
+
" call.args = args\n" +
|
26
|
+
" @instance_history << call\n" +
|
27
|
+
" return #{var_name}.size == 0 ? nil : #{var_name}.shift\n" +
|
28
|
+
" end\n" +
|
29
|
+
"}\n"
|
30
|
+
eval(def_string)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_initialize(method_names)
|
34
|
+
def_string = "@klass.class_eval {\n" +
|
35
|
+
" def initialize(*args, &block)\n" +
|
36
|
+
" @instance_history = []\n"
|
37
|
+
method_names.each do |method, values|
|
38
|
+
def_string << " @__#{method.to_s}_rv = nil\n"
|
39
|
+
end
|
40
|
+
def_string << (" end\n" + "}\n")
|
41
|
+
eval(def_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_instance(return_values)
|
45
|
+
mock = @klass.new
|
46
|
+
return_values.each do |method, value|
|
47
|
+
mock.instance_variable_set(eval(":@___#{method}_rv"), value)
|
48
|
+
end
|
49
|
+
return mock
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Name: Mockery::RecordControl (lib/mockery/record_control.rb)
|
4
|
+
#
|
5
|
+
# Description:
|
6
|
+
#
|
7
|
+
# The RecordControl class enables recording to be 'paused'
|
8
|
+
# inside a 'try' block.
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# Copyright 2005 Gary Shea
|
12
|
+
#
|
13
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
14
|
+
# you may not use this file except in compliance with the License.
|
15
|
+
# You may obtain a copy of the License at
|
16
|
+
#
|
17
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
18
|
+
#
|
19
|
+
# Unless required by applicable law or agreed to in writing, software
|
20
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
21
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
22
|
+
# See the License for the specific language governing permissions and
|
23
|
+
# limitations under the License.
|
24
|
+
#
|
25
|
+
|
26
|
+
module Mockery
|
27
|
+
|
28
|
+
class RecordControl
|
29
|
+
|
30
|
+
def initialize(mocks)
|
31
|
+
@mocks = mocks
|
32
|
+
end
|
33
|
+
|
34
|
+
def pause(&block)
|
35
|
+
self.stop
|
36
|
+
block.call
|
37
|
+
self.start
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@mocks.each do |mock|
|
42
|
+
mock.__record_state = false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def start
|
47
|
+
@mocks.each do |mock|
|
48
|
+
mock.__record_state = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|