rd_stump 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-10-03
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jeremy McAnally
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,23 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.rdoc
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/stump.rb
10
+ lib/stump/metaid.rb
11
+ lib/stump/mock.rb
12
+ lib/stump/mocks.rb
13
+ lib/stump/proxy.rb
14
+ lib/stump/stub.rb
15
+ lib/stump/core_ext/test_case.rb
16
+ lib/stump/version.rb
17
+ setup.rb
18
+ tasks/deployment.rake
19
+ tasks/environment.rake
20
+ test/test_helper.rb
21
+ test/test_proxy.rb
22
+ test/test_mock.rb
23
+ test/test_stub.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on stump, see http://stump.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ = stump
2
+
3
+ * http://www.github.com/jeremymcanally/stump
4
+
5
+ == DESCRIPTION:
6
+
7
+ Stubbing and mocking that isn't painful, fanciful, and doesn't inspire any sort of contempt or bitterness.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Stubbing right on the object.
12
+
13
+ # Returns 'hey!'
14
+ MyClass.stub!(:thing, :return => "hey!")
15
+ # Returns nil
16
+ your_object.stub!(:hello)
17
+
18
+ * Pure stub objects.
19
+
20
+ my_stub = stub(:thing, :return => "dude, a thing!")
21
+ my_stub.thing # => "dude, a thing!"
22
+
23
+ * Mocking right on the object.
24
+
25
+ my_object = "things are fun"
26
+ my_object.mock!(:fancy, "ooo fancy!")
27
+ my_object.fancy # => "ooo fancy!"
28
+
29
+ * Pure mock objects.
30
+
31
+ my_mock = mock(:hello, :return => "what fun is this?")
32
+ my_mock.hello # => "what fun is this?"
33
+
34
+ * Proxying objects.
35
+
36
+ class Greeting
37
+ def bonjour
38
+ "Bonjour!"
39
+ end
40
+ end
41
+
42
+ greet_me = Greeting.new
43
+ greet_me.proxy!(:bonjour)
44
+ greet_me.bonjour # => "Bonjour!"
45
+
46
+ greet_you = Greeting.new
47
+ greet_you.proxy!(:bonjour)
48
+
49
+ # finish test...
50
+ # ! Unmet expectations: #<Greeting:0x371d24> expected hello
51
+
52
+ == SYNOPSIS:
53
+
54
+ # Stub class methods
55
+ MyClass.stub!(:thing, :return => "hey!")
56
+ # Stub method on object
57
+ your_object.stub!(:my_method)
58
+ # Stub method on object, requiring parameters
59
+ my_object.stub!(:other_method) do |arg1, arg2|
60
+ puts arg1
61
+ puts arg2
62
+ end
63
+
64
+ # Mock method on object
65
+ MyClass.mock!(:my_method, :return => "hehe")
66
+ # Mock method on object, requiring parameters
67
+ my_obj.mock!(:hehe, :return => nil) do |arg1, arg2, arg3|
68
+ # Fun party!
69
+ end
70
+
71
+ # Sets expectation it will receive a method, calling the real method
72
+ MyClass.proxy!(:the_method)
73
+ # Returns static value but still has side effects of real method call
74
+ MyClass.proxy!(:that_method, :return => "poop")
75
+
76
+ == NOTES:
77
+
78
+ * Be sure you call +super+ in +teardown+ if you create your own +teardown+ methods. Otherwise your mocks won't get verified!
79
+
80
+ == REQUIREMENTS:
81
+
82
+ * Test::Unit (batteries included with Ruby)
83
+
84
+ == INSTALL:
85
+
86
+ $ gem sources -a http://gems.github.com
87
+ $ sudo gem install jeremymcanally-stump
88
+
89
+
90
+ == ACKNOWLEDGEMENTS:
91
+
92
+ * Thanks to Jim Weirich for Flexmock, David Chelmisky et. al. for RSpec's mocking stuff, and Brian Takita for rr. All of those things fed into stump.
93
+ * BIG thanks to Nathan Sutton (fowlduck) for helping me work through how to get +proxy!+ to work with ActiveRecord/method_missing.
94
+
95
+ == LICENSE:
96
+
97
+ (The MIT License)
98
+
99
+ Copyright (c) 2008 Jeremy McAnally
100
+
101
+ Permission is hereby granted, free of charge, to any person obtaining
102
+ a copy of this software and associated documentation files (the
103
+ 'Software'), to deal in the Software without restriction, including
104
+ without limitation the rights to use, copy, modify, merge, publish,
105
+ distribute, sublicense, and/or sell copies of the Software, and to
106
+ permit persons to whom the Software is furnished to do so, subject to
107
+ the following conditions:
108
+
109
+ The above copyright notice and this permission notice shall be
110
+ included in all copies or substantial portions of the Software.
111
+
112
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
113
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
114
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
115
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
116
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
117
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
118
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'stump/version'
2
+
3
+ AUTHOR = 'Jeremy McAnally' # can also be an array of Authors
4
+ EMAIL = "jeremy@entp.com"
5
+ DESCRIPTION = "Stubbing and mocking that isn't painful."
6
+ GEM_NAME = 'stump' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'stump' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ # ['activesupport', '>= 1.3.1']
12
+ ] # An array of rubygem dependencies [name, version]
13
+
14
+ @config_file = "~/.rubyforge/user-config.yml"
15
+ @config = nil
16
+ RUBYFORGE_USERNAME = "unknown"
17
+ def rubyforge_username
18
+ unless @config
19
+ begin
20
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
21
+ rescue
22
+ puts <<-EOS
23
+ ERROR: No rubyforge config file found: #{@config_file}
24
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
25
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
26
+ EOS
27
+ exit
28
+ end
29
+ end
30
+ RUBYFORGE_USERNAME.replace @config["username"]
31
+ end
32
+
33
+
34
+ REV = nil
35
+ # UNCOMMENT IF REQUIRED:
36
+ # REV = YAML.load(`svn info`)['Revision']
37
+ VERS = Stump::VERSION::STRING + (REV ? ".#{REV}" : "")
38
+ RDOC_OPTS = ['--quiet', '--title', 'stump documentation',
39
+ "--opname", "index.html",
40
+ "--line-numbers",
41
+ "--main", "README",
42
+ "--inline-source"]
43
+
44
+ class Hoe
45
+ def extra_deps
46
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
47
+ @extra_deps
48
+ end
49
+ end
50
+
51
+ # Generate all the Rake tasks
52
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
53
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
54
+ p.developer(AUTHOR, EMAIL)
55
+ p.description = DESCRIPTION
56
+ p.summary = DESCRIPTION
57
+ p.url = HOMEPATH
58
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
59
+ p.test_globs = ["test/**/test_*.rb"]
60
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
61
+
62
+ # == Optional
63
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
64
+ #p.extra_deps = EXTRA_DEPENDENCIES
65
+
66
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
67
+ end
68
+
69
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
70
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
71
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
72
+ $hoe.rsync_args = '-av --delete --ignore-errors'
73
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
data/lib/stump.rb ADDED
@@ -0,0 +1,11 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'stump/version'
5
+ require 'stump/metaid'
6
+ require 'stump/core_ext/test_case'
7
+
8
+ require 'stump/stub'
9
+ require 'stump/mocks'
10
+ require 'stump/mock'
11
+ require 'stump/proxy'
@@ -0,0 +1,21 @@
1
+ module Test
2
+ module Unit
3
+ class TestCase
4
+ def stumpdown!
5
+ begin
6
+ if !Stump::Mocks.failures.nil? && !Stump::Mocks.failures.empty?
7
+ fails = Stump::Mocks.failures.map {|object, method| "#{object.inspect} expected #{method}"}.join(", ")
8
+
9
+ flunk "Unmet expectations: #{fails}"
10
+ end
11
+ ensure
12
+ Stump::Mocks.clear!
13
+ end
14
+ end
15
+
16
+ def teardown
17
+ stumpdown!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # thanks _why
2
+ # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
3
+ class Object
4
+ # The hidden singleton lurks behind everyone
5
+ def metaclass; class << self; self; end; end
6
+ def meta_eval &blk; metaclass.instance_eval &blk; end
7
+
8
+ # Adds methods to a metaclass
9
+ def meta_def name, &blk
10
+ meta_eval { define_method name, &blk }
11
+ end
12
+
13
+ # Defines an instance method within a class
14
+ def class_def name, &blk
15
+ class_eval { define_method name, &blk }
16
+ end
17
+ end
data/lib/stump/mock.rb ADDED
@@ -0,0 +1,51 @@
1
+ class Object
2
+ # Create a mock method on an object. A mock object will place an expectation
3
+ # on behavior and cause a test failure if it's not fulfilled.
4
+ #
5
+ # ==== Examples
6
+ #
7
+ # my_string = "a wooden rabbit"
8
+ # my_string.mock!(:retreat!, :return => "run away! run away!")
9
+ # my_string.mock!(:question, :return => "what is the airspeed velocity of an unladen sparrow?")
10
+ #
11
+ # # test/your_test.rb
12
+ # my_string.retreat! # => "run away! run away!"
13
+ # # If we let the test case end at this point, it fails with:
14
+ # # Unmet expectation: #<Sparrow:1ee7> expected question
15
+ #
16
+ def mock!(method, options = {}, &block)
17
+ Stump::Mocks.add([self, method])
18
+
19
+ behavior = if block_given?
20
+ lambda do |*args|
21
+ raise ArgumentError if block.arity >= 0 && args.length != block.arity
22
+
23
+ Stump::Mocks.verify([self, method])
24
+ block.call(args)
25
+ end
26
+ else
27
+ lambda do |*args|
28
+ Stump::Mocks.verify([self, method])
29
+ return options[:return]
30
+ end
31
+ end
32
+
33
+ meta_def method, &behavior
34
+ end
35
+ end
36
+
37
+ module Kernel
38
+ # Create a pure mock object rather than mocking specific methods on an object.
39
+ #
40
+ # ==== Examples
41
+ #
42
+ # my_mock = mock(:thing, :return => "whee!")
43
+ # my_mock.thing # => "whee"
44
+ #
45
+ def mock(method, options = {}, &block)
46
+ mock_object = Object.new
47
+ mock_object.mock!(method, options, &block)
48
+ mock_object
49
+ end
50
+ end
51
+
@@ -0,0 +1,23 @@
1
+ module Stump
2
+ # A class to track the mocks and proxies that have been satisfied
3
+ class Mocks
4
+ class <<self
5
+ def add(mock)
6
+ @mocks ||= []
7
+ @mocks << mock
8
+ end
9
+
10
+ def verify(mock)
11
+ @mocks.delete(mock)
12
+ end
13
+
14
+ def failures
15
+ @mocks
16
+ end
17
+
18
+ def clear!
19
+ @mocks = []
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,115 @@
1
+ class Object
2
+ # Creates a proxy method on an object. In this setup, it places an expectation on an object (like a mock)
3
+ # but still calls the original method. So if you want to make sure the method is called and still return
4
+ # its value, or simply want to invoke the side effects of a method and return a stubbed value, then you can
5
+ # do that.
6
+ #
7
+ # ==== Examples
8
+ #
9
+ # class Parrot
10
+ # def speak!
11
+ # puts @words
12
+ # end
13
+ #
14
+ # def say_this(words)
15
+ # @words = words
16
+ # "I shall say #{words}!"
17
+ # end
18
+ # end
19
+ #
20
+ # # => test/your_test.rb
21
+ # sqawky = Parrot.new
22
+ # sqawky.proxy!(:say_this)
23
+ # # Proxy method still calls original method...
24
+ # sqawky.say_this("hey") # => "I shall say hey!"
25
+ # sqawky.speak! # => "hey"
26
+ #
27
+ # sqawky.proxy!(:say_this, "herro!")
28
+ # # Even though we return a stubbed value...
29
+ # sqawky.say_this("these words") # => "herro!"
30
+ # # ...the side effects are still there.
31
+ # sqawky.speak! # => "these words"
32
+ #
33
+ # TODO: This implementation is still very rough. Needs refactoring and refining. Won't
34
+ # work on ActiveRecord attributes, for example.
35
+ #
36
+ def proxy!(method, options = {}, &block)
37
+ Stump::Mocks.add([self, method])
38
+
39
+ if respond_to?(method)
40
+ proxy_existing_method(method, options, &block)
41
+ else
42
+ proxy_missing_method(method, options, &block)
43
+ end
44
+ end
45
+
46
+ protected
47
+ def proxy_existing_method(method, options = {}, &block)
48
+ method_alias = "__old_#{method}"
49
+
50
+ meta_eval do
51
+ module_eval("alias #{method_alias} #{method}")
52
+ end
53
+
54
+ check_arity = Proc.new do |args|
55
+ arity = method(method_alias.to_sym).arity
56
+ if ((arity >= 0) ?
57
+ (args.length != arity) :
58
+ (args.length < ~arity))
59
+ # Negative arity means some params are optional, so we check
60
+ # for the minimum required. Sadly, we can't tell what the
61
+ # maximum is.
62
+ raise ArgumentError
63
+ end
64
+ end
65
+
66
+ behavior = if options[:return]
67
+ lambda do |*args|
68
+ check_arity.call(args)
69
+
70
+ Stump::Mocks.verify([self, method])
71
+
72
+ if method(method_alias.to_sym).arity == 0
73
+ send(method_alias)
74
+ else
75
+ send(method_alias, *args)
76
+ end
77
+
78
+ return options[:return]
79
+ end
80
+ else
81
+ lambda do |*args|
82
+ check_arity.call(args)
83
+
84
+ Stump::Mocks.verify([self, method])
85
+
86
+ if method(method_alias.to_sym).arity == 0
87
+ return send(method_alias)
88
+ else
89
+ return send(method_alias, *args)
90
+ end
91
+ end
92
+ end
93
+
94
+ meta_def method, &behavior
95
+ end
96
+
97
+ def proxy_missing_method(method, options = {}, &block)
98
+ behavior = if options[:return]
99
+ lambda do |*args|
100
+ Stump::Mocks.verify([self, method])
101
+
102
+ method_missing(method, args)
103
+ return options[:return]
104
+ end
105
+ else
106
+ lambda do |*args|
107
+ Stump::Mocks.verify([self, method])
108
+
109
+ method_missing(method, args)
110
+ end
111
+ end
112
+
113
+ meta_def method, &behavior
114
+ end
115
+ end