not_a_mock 1.0.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/.gitignore +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +82 -0
- data/Rakefile +38 -0
- data/TODO +49 -0
- data/VERSION +1 -0
- data/lib/not_a_mock/active_record_extensions.rb +12 -0
- data/lib/not_a_mock/argument_constraint_extensions.rb +32 -0
- data/lib/not_a_mock/call_recorder.rb +88 -0
- data/lib/not_a_mock/matchers/anything_matcher.rb +28 -0
- data/lib/not_a_mock/matchers/args_matcher.rb +74 -0
- data/lib/not_a_mock/matchers/call_matcher.rb +64 -0
- data/lib/not_a_mock/matchers/method_matcher.rb +29 -0
- data/lib/not_a_mock/matchers/result_matcher.rb +29 -0
- data/lib/not_a_mock/matchers/times_matcher.rb +46 -0
- data/lib/not_a_mock/matchers.rb +68 -0
- data/lib/not_a_mock/object_extensions.rb +105 -0
- data/lib/not_a_mock/rspec_mock_framework_adapter.rb +16 -0
- data/lib/not_a_mock/stub.rb +40 -0
- data/lib/not_a_mock/stubber.rb +69 -0
- data/lib/not_a_mock.rb +14 -0
- data/spec/call_recording_spec.rb +68 -0
- data/spec/matchers_spec.rb +217 -0
- data/spec/stub_active_record_spec.rb +29 -0
- data/spec/stub_instance_spec.rb +49 -0
- data/spec/stub_method_spec.rb +242 -0
- metadata +85 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
# Call this on any object or class with a list of method names. Any future
|
4
|
+
# calls to those methods will be recorded in NotAMock::CallRecorder.
|
5
|
+
#
|
6
|
+
# See NotAMock::Matchers for info on how to test which methods have been
|
7
|
+
# called, with what arguments, etc.
|
8
|
+
def track_methods(*methods)
|
9
|
+
if methods.empty?
|
10
|
+
self.public_methods(false).map {|method_name| methods << method_name.to_s.to_sym}
|
11
|
+
end
|
12
|
+
|
13
|
+
methods.each do |method|
|
14
|
+
NotAMock::CallRecorder.instance.track_method(self, method)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
alias_method(:track_method, :track_methods)
|
18
|
+
alias_method(:log_calls_to, :track_methods) # For backwards compatibility.
|
19
|
+
|
20
|
+
# Stop recording calls for the given methods.
|
21
|
+
def untrack_methods(*methods)
|
22
|
+
methods.each do |method|
|
23
|
+
NotAMock::CallRecorder.instance.untrack_method(self, method)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias_method(:untrack_method, :untrack_methods)
|
27
|
+
|
28
|
+
# If passed a symbol and a block, this replaces the named method on this
|
29
|
+
# object with a stub version that evaluates the block and returns the result.
|
30
|
+
#
|
31
|
+
# If passed a hash, this is an alias for stub_methods.
|
32
|
+
#
|
33
|
+
# Calls to stubbed methods are recorded in the NotAMock::CallRecorder,
|
34
|
+
# so you can later make assertions about them as described in
|
35
|
+
# NotAMock::Matchers.
|
36
|
+
def stub_method(method, &block)
|
37
|
+
case method
|
38
|
+
when Symbol
|
39
|
+
NotAMock::CallRecorder.instance.untrack_method(self, method)
|
40
|
+
NotAMock::Stubber.instance.unstub_method(self, method)
|
41
|
+
NotAMock::Stubber.instance.stub_method(self, method, &block)
|
42
|
+
NotAMock::CallRecorder.instance.track_method(self, method)
|
43
|
+
when Hash
|
44
|
+
stub_methods(method)
|
45
|
+
else
|
46
|
+
raise ArgumentError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Takes a hash of method names mapped to results, and replaces each named
|
51
|
+
# method on this object with a stub version returning the corresponding result.
|
52
|
+
#
|
53
|
+
# Calls to stubbed methods are recorded in the NotAMock::CallRecorder,
|
54
|
+
# so you can later make assertions about them as described in
|
55
|
+
# NotAMock::Matchers.
|
56
|
+
def stub_methods(methods)
|
57
|
+
methods.each do |method, result|
|
58
|
+
stub_method(method) {|*args| result }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Takes a hash of method names mapped to exceptions, and replaces each named
|
63
|
+
# method on this object with a stub version returning the corresponding exception.
|
64
|
+
def stub_methods_to_raise(methods)
|
65
|
+
methods.each do |method, exception|
|
66
|
+
stub_method(method) {|*args| raise exception }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
alias_method(:stub_method_to_raise, :stub_methods_to_raise)
|
70
|
+
|
71
|
+
# Removes the stubbed versions of the given methods and restores the
|
72
|
+
# original methods.
|
73
|
+
def unstub_methods(*methods)
|
74
|
+
methods.each do |method, result|
|
75
|
+
NotAMock::CallRecorder.instance.untrack_method(self, method)
|
76
|
+
NotAMock::Stubber.instance.unstub_method(self, method)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
alias_method(:unstub_method, :unstub_methods)
|
80
|
+
|
81
|
+
class << self
|
82
|
+
# Called on a class, creates a stub instance of that class. Takes a hash of
|
83
|
+
# method names and their returns values, and creates those methods on the new
|
84
|
+
# stub instance.
|
85
|
+
#
|
86
|
+
# See NotAMock::Stub for more details about the returned objects.
|
87
|
+
def stub_instance(methods = {})
|
88
|
+
NotAMock::Stub.new(self, methods)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the metaclass of this object. For an explanation of metaclasses, see:
|
93
|
+
# http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
94
|
+
def metaclass
|
95
|
+
class << self
|
96
|
+
self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Evaluates the block in the context of this object's metaclass.
|
101
|
+
def meta_eval(&block)
|
102
|
+
metaclass.instance_eval(&block)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module NotAMock
|
2
|
+
module RspecMockFrameworkAdapter
|
3
|
+
|
4
|
+
def setup_mocks_for_rspec
|
5
|
+
end
|
6
|
+
|
7
|
+
def verify_mocks_for_rspec
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown_mocks_for_rspec
|
11
|
+
NotAMock::CallRecorder.instance.reset
|
12
|
+
NotAMock::Stubber.instance.reset
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NotAMock
|
2
|
+
# Instances returned by Object.stub_instance are NotAMock::Stub objects.
|
3
|
+
# These do their best to masquerade as the real thing.
|
4
|
+
class Stub
|
5
|
+
|
6
|
+
# This is normall only called from Object.stub_instance.
|
7
|
+
def initialize(stubbed_class, methods = {}) #:nodoc:
|
8
|
+
@stubbed_class = stubbed_class
|
9
|
+
methods.each do |method, result|
|
10
|
+
self.meta_eval do
|
11
|
+
define_method(method) { result }
|
12
|
+
end
|
13
|
+
track_method(method)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns "Stub StubbedClass".
|
18
|
+
def inspect
|
19
|
+
"Stub #{@stubbed_class.to_s}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns true if the class of the stubbed object or one of its superclasses is klass.
|
23
|
+
def is_a?(klass)
|
24
|
+
@stubbed_class.ancestors.include?(klass)
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :kind_of?, :is_a?
|
28
|
+
|
29
|
+
# Returns true if the class of the stubbed object is klass.
|
30
|
+
def instance_of?(klass)
|
31
|
+
@stubbed_class == klass
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the class of the stubbed object.
|
35
|
+
def class
|
36
|
+
@stubbed_class
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'not_a_mock/object_extensions'
|
3
|
+
|
4
|
+
module NotAMock
|
5
|
+
# The Stubber is a singleton that keeps track of all the stub methods
|
6
|
+
# installed in any object.
|
7
|
+
class Stubber
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@stubbed_methods = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Stub +method+ on +object+ to evalutate +block+ and return the result.
|
15
|
+
#
|
16
|
+
# You should call Object#stub_method rathing than calling this directly.
|
17
|
+
def stub_method(object, method, &block) #:nodoc:
|
18
|
+
unless @stubbed_methods.include?([object, method])
|
19
|
+
@stubbed_methods << [object, method]
|
20
|
+
add_hook(object, method, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Remove the stubbed +method+ on +object+.
|
25
|
+
#
|
26
|
+
# You should call Object#unstub_methods rather than calling this directly.
|
27
|
+
def unstub_method(object, method) #:nodoc:
|
28
|
+
if @stubbed_methods.delete([object, method])
|
29
|
+
remove_hook(object, method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Removes all stub methods.
|
34
|
+
def reset
|
35
|
+
@stubbed_methods.each do |object, method|
|
36
|
+
remove_hook(object, method)
|
37
|
+
end
|
38
|
+
@stubbed_methods = []
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def add_hook(object, method, &block)
|
44
|
+
method_exists = method_at_any_level?(object, method.to_s)
|
45
|
+
object.meta_eval do
|
46
|
+
alias_method("__unstubbed_#{method}", method) if method_exists
|
47
|
+
define_method(method, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_hook(object, method)
|
52
|
+
method_exists = method_at_any_level?(object, "__unstubbed_#{method}")
|
53
|
+
object.meta_eval do
|
54
|
+
if method_exists
|
55
|
+
alias_method(method, "__unstubbed_#{method}")
|
56
|
+
remove_method("__unstubbed_#{method}")
|
57
|
+
else
|
58
|
+
remove_method(method)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_at_any_level?(object, method)
|
64
|
+
object.methods.include?(method) ||
|
65
|
+
object.protected_methods.include?(method) ||
|
66
|
+
object.private_methods.include?(method)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/not_a_mock.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'not_a_mock/active_record_extensions'
|
2
|
+
require 'not_a_mock/argument_constraint_extensions'
|
3
|
+
require 'not_a_mock/call_recorder'
|
4
|
+
require 'not_a_mock/matchers'
|
5
|
+
require 'not_a_mock/matchers/anything_matcher'
|
6
|
+
require 'not_a_mock/matchers/args_matcher'
|
7
|
+
require 'not_a_mock/matchers/call_matcher'
|
8
|
+
require 'not_a_mock/matchers/method_matcher'
|
9
|
+
require 'not_a_mock/matchers/result_matcher'
|
10
|
+
require 'not_a_mock/matchers/times_matcher'
|
11
|
+
require 'not_a_mock/object_extensions'
|
12
|
+
require 'not_a_mock/rspec_mock_framework_adapter'
|
13
|
+
require 'not_a_mock/stubber'
|
14
|
+
require 'not_a_mock/stub'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'not_a_mock'
|
3
|
+
|
4
|
+
class TrackedClass
|
5
|
+
def my_method(argument)
|
6
|
+
"result"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "A recorded method" do
|
11
|
+
|
12
|
+
before do
|
13
|
+
@recorder = NotAMock::CallRecorder.instance
|
14
|
+
@object = TrackedClass.new
|
15
|
+
@object.track_method(:my_method)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should record a method call" do
|
19
|
+
@object.my_method("argument")
|
20
|
+
|
21
|
+
@recorder.calls.should include(:object => @object, :method => :my_method, :args => ["argument"], :result => "result")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not record calls after untrack_method" do
|
25
|
+
@object.my_method("argument 1")
|
26
|
+
@object.untrack_method(:my_method)
|
27
|
+
@object.my_method("argument 2")
|
28
|
+
|
29
|
+
@recorder.calls.should include(:object => @object, :method => :my_method, :args => ["argument 1"], :result => "result")
|
30
|
+
@recorder.calls.should_not include(:object => @object, :method => :my_method, :args => ["argument 2"], :result => "result")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not record calls after stop_all_recording" do
|
34
|
+
@object.track_method(:my_method)
|
35
|
+
@object.my_method("argument 1")
|
36
|
+
@recorder.untrack_all
|
37
|
+
@object.my_method("argument 2")
|
38
|
+
|
39
|
+
@recorder.calls.should include(:object => @object, :method => :my_method, :args => ["argument 1"], :result => "result")
|
40
|
+
@recorder.calls.should_not include(:object => @object, :method => :my_method, :args => ["argument 2"], :result => "result")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not break when track_method is called again" do
|
44
|
+
@object.track_method(:my_method)
|
45
|
+
@object.my_method("argument 1")
|
46
|
+
@object.untrack_method(:my_method)
|
47
|
+
@object.my_method("argument 2")
|
48
|
+
|
49
|
+
@recorder.calls.should include(:object => @object, :method => :my_method, :args => ["argument 1"], :result => "result")
|
50
|
+
@recorder.calls.should_not include(:object => @object, :method => :my_method, :args => ["argument 2"], :result => "result")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should not break when untrack_method is called more than once" do
|
54
|
+
@object.track_method(:my_method)
|
55
|
+
@object.my_method("argument 1")
|
56
|
+
@object.untrack_method(:my_method)
|
57
|
+
@object.untrack_method(:my_method)
|
58
|
+
@object.my_method("argument 2")
|
59
|
+
|
60
|
+
@recorder.calls.should include(:object => @object, :method => :my_method, :args => ["argument 1"], :result => "result")
|
61
|
+
@recorder.calls.should_not include(:object => @object, :method => :my_method, :args => ["argument 2"], :result => "result")
|
62
|
+
end
|
63
|
+
|
64
|
+
after do
|
65
|
+
@recorder.reset
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'not_a_mock'
|
3
|
+
|
4
|
+
class TrackedClass < Object
|
5
|
+
def initialize(*calls)
|
6
|
+
calls.each do |call|
|
7
|
+
NotAMock::CallRecorder.instance.calls << call.merge(:object => self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"TrackedClass"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe NotAMock::Matchers::AnythingMatcher do
|
17
|
+
|
18
|
+
it "should match if a method was called" do
|
19
|
+
@object = TrackedClass.new({ :method => :length, :args => [], :result => nil })
|
20
|
+
@matcher = have_been_called
|
21
|
+
@matcher.matches?(@object).should be_true
|
22
|
+
@matcher.negative_failure_message.should == "TrackedClass was called"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not match if a method wasn't called" do
|
26
|
+
@object = TrackedClass.new
|
27
|
+
@matcher = have_been_called
|
28
|
+
@matcher.matches?(@object).should be_false
|
29
|
+
@matcher.failure_message.should == "TrackedClass was never called"
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
NotAMock::CallRecorder.instance.reset
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe NotAMock::Matchers::MethodMatcher do
|
39
|
+
|
40
|
+
it "should match a called method" do
|
41
|
+
@object = TrackedClass.new({ :method => :length, :args => [], :result => nil })
|
42
|
+
@matcher = have_received(:length)
|
43
|
+
@matcher.matches?(@object).should be_true
|
44
|
+
@matcher.negative_failure_message.should == "TrackedClass received length"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not match an uncalled method" do
|
48
|
+
@object = TrackedClass.new
|
49
|
+
@matcher = have_received(:width)
|
50
|
+
@matcher.matches?(@object).should be_false
|
51
|
+
@matcher.failure_message.should == "TrackedClass didn't receive width"
|
52
|
+
end
|
53
|
+
|
54
|
+
after do
|
55
|
+
NotAMock::CallRecorder.instance.reset
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe NotAMock::Matchers::ArgsMatcher, "matching calls with arguments " do
|
61
|
+
|
62
|
+
before do
|
63
|
+
@object = TrackedClass.new({ :method => :length, :args => [1, [2, 3, 4], 5], :result => nil })
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should match a called method with the correct arguments" do
|
67
|
+
@matcher = have_received(:length).with(1, [2, 3, 4], 5)
|
68
|
+
@matcher.matches?(@object).should be_true
|
69
|
+
@matcher.negative_failure_message.should == "TrackedClass received length, with args [1, [2, 3, 4], 5]"
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not match a called method with the wrong arguments" do
|
73
|
+
@matcher = have_received(:length).with(3, 2, 1)
|
74
|
+
@matcher.matches?(@object).should be_false
|
75
|
+
@matcher.failure_message.should == "TrackedClass received length, but not with args [3, 2, 1]"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should match a called method with a wildcard argument" do
|
79
|
+
@matcher = have_received(:length).with(1, anything, 5)
|
80
|
+
@matcher.matches?(@object).should be_true
|
81
|
+
@matcher.matches?(@object).should be_true
|
82
|
+
@matcher.negative_failure_message.should == "TrackedClass received length, with args [1, anything, 5]"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should match a called method with a wildcard argument" do
|
86
|
+
@matcher = have_received(:length).with(1, in_any_order([4, 3, 2]), 5)
|
87
|
+
@matcher.matches?(@object).should be_true
|
88
|
+
@matcher.matches?(@object).should be_true
|
89
|
+
@matcher.negative_failure_message.should == "TrackedClass received length, with args [1, in_any_order([4, 3, 2]), 5]"
|
90
|
+
end
|
91
|
+
|
92
|
+
after do
|
93
|
+
NotAMock::CallRecorder.instance.reset
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
describe NotAMock::Matchers::ArgsMatcher, "matching calls without arguments" do
|
99
|
+
|
100
|
+
before do
|
101
|
+
@object = TrackedClass.new(
|
102
|
+
{ :method => :length, :args => [1, 2, 3], :result => nil },
|
103
|
+
{ :method => :width, :args => [], :result => nil }
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should match a method called without arguments" do
|
108
|
+
@matcher = have_received(:width).without_args
|
109
|
+
@matcher.matches?(@object).should be_true
|
110
|
+
@matcher.negative_failure_message.should == "TrackedClass received width, without args"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not match a method called with arguments" do
|
114
|
+
@matcher = have_received(:length).without_args
|
115
|
+
@matcher.matches?(@object).should be_false
|
116
|
+
@matcher.failure_message.should == "TrackedClass received length, but not without args"
|
117
|
+
end
|
118
|
+
|
119
|
+
after do
|
120
|
+
NotAMock::CallRecorder.instance.reset
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
describe NotAMock::Matchers::ResultMatcher do
|
126
|
+
|
127
|
+
before do
|
128
|
+
@object = TrackedClass.new(
|
129
|
+
{ :method => :length, :args => [], :result => 13 },
|
130
|
+
{ :method => :width, :args => [], :result => 42 }
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should match a method that returned the correct result" do
|
135
|
+
@matcher = have_received(:length).and_returned(13)
|
136
|
+
@matcher.matches?(@object).should be_true
|
137
|
+
@matcher.negative_failure_message.should == "TrackedClass received length, and returned 13"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should not match a method the returned the wrong result" do
|
141
|
+
@matcher = have_received(:width).and_returned(13)
|
142
|
+
@matcher.matches?(@object).should be_false
|
143
|
+
@matcher.failure_message.should == "TrackedClass received width, but didn't return 13"
|
144
|
+
end
|
145
|
+
|
146
|
+
after do
|
147
|
+
NotAMock::CallRecorder.instance.reset
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
describe NotAMock::Matchers::TimesMatcher do
|
153
|
+
|
154
|
+
before do
|
155
|
+
@object = TrackedClass.new(
|
156
|
+
{ :method => :once, :args => [], :result => nil },
|
157
|
+
{ :method => :twice, :args => [], :result => nil },
|
158
|
+
{ :method => :twice, :args => [], :result => nil },
|
159
|
+
{ :method => :thrice, :args => [], :result => nil },
|
160
|
+
{ :method => :thrice, :args => [], :result => nil },
|
161
|
+
{ :method => :thrice, :args => [], :result => nil }
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should match a method that was called once" do
|
166
|
+
@matcher = have_received(:once).once
|
167
|
+
@matcher.matches?(@object).should be_true
|
168
|
+
@matcher.negative_failure_message.should == "TrackedClass received once, once"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should match a method that was called twice" do
|
172
|
+
@matcher = have_received(:twice).twice
|
173
|
+
@matcher.matches?(@object).should be_true
|
174
|
+
@matcher.negative_failure_message.should == "TrackedClass received twice, twice"
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should match a method that was called 3 times" do
|
178
|
+
@matcher = have_received(:thrice).exactly(3).times
|
179
|
+
@matcher.matches?(@object).should be_true
|
180
|
+
@matcher.negative_failure_message.should == "TrackedClass received thrice, 3 times"
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should not match a method a method that was called the wrong number of times" do
|
184
|
+
@matcher = have_received(:thrice).once
|
185
|
+
@matcher.matches?(@object).should be_false
|
186
|
+
@matcher.failure_message.should == "TrackedClass received thrice, but 3 times"
|
187
|
+
end
|
188
|
+
|
189
|
+
after do
|
190
|
+
NotAMock::CallRecorder.instance.reset
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "A chain of matchers" do
|
196
|
+
|
197
|
+
before do
|
198
|
+
@object = TrackedClass.new({ :method => :length, :args => [1, 2, 3], :result => 42 })
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should match the correct method, args, and result" do
|
202
|
+
@matcher = have_received(:length).with(1, 2, 3).and_returned(42)
|
203
|
+
@matcher.matches?(@object).should be_true
|
204
|
+
@matcher.negative_failure_message.should == "TrackedClass received length, with args [1, 2, 3], and returned 42"
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should not match the correct method, but with the incorrect args" do
|
208
|
+
@matcher = have_received(:length).with(3, 2, 1).and_returned(42)
|
209
|
+
@matcher.matches?(@object).should be_false
|
210
|
+
@matcher.failure_message.should == "TrackedClass received length, but not with args [3, 2, 1]"
|
211
|
+
end
|
212
|
+
|
213
|
+
after do
|
214
|
+
NotAMock::CallRecorder.instance.reset
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'not_a_mock'
|
3
|
+
|
4
|
+
class ExampleActiveRecord < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "A stubbed ActiveRecord object" do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@example = ExampleActiveRecord.stub_instance
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return a valid id" do
|
14
|
+
lambda { @example.id }.should_not raise_error(NoMethodError)
|
15
|
+
@example.id.should be_an_instance_of(Fixnum)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return the id as a string for to_param" do
|
19
|
+
lambda { @example.to_param }.should_not raise_error(NoMethodError)
|
20
|
+
@example.to_param.should be_an_instance_of(String)
|
21
|
+
@example.to_param.should == @example.id.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
after do
|
25
|
+
NotAMock::CallRecorder.instance.reset
|
26
|
+
NotAMock::Stubber.instance.reset
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
require 'not_a_mock'
|
3
|
+
|
4
|
+
describe "A stub instance" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@object = String.stub_instance(:length => 42, :id => 99)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the right result for a stubbed method" do
|
11
|
+
@object.length.should == 42
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return its name when inspected" do
|
15
|
+
@object.inspect.should == "Stub String"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should handle the id method being stubbed" do
|
19
|
+
@object.id.should == 99
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise an error when a method that's not stubbed is called" do
|
23
|
+
lambda { @object.whatever }.should raise_error(NoMethodError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should record a call to a stubbed method" do
|
27
|
+
@object.length
|
28
|
+
NotAMock::CallRecorder.instance.calls.should include(:object => @object, :method => :length, :args => [], :result => 42)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow adding of new stubbed methods" do
|
32
|
+
@object.stub_method(:width => 7)
|
33
|
+
@object.width.should == 7
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should identify itself as the underlying object" do
|
37
|
+
@object.is_a?(String).should be_true
|
38
|
+
@object.should be_kind_of(String)
|
39
|
+
@object.should be_kind_of(Object)
|
40
|
+
@object.should be_instance_of(String)
|
41
|
+
@object.class.should == String
|
42
|
+
end
|
43
|
+
|
44
|
+
after do
|
45
|
+
NotAMock::CallRecorder.instance.reset
|
46
|
+
NotAMock::Stubber.instance.reset
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|