flexmock 1.2.0 → 1.3.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.
@@ -0,0 +1,165 @@
1
+ = FlexMock 1.3.0 Released
2
+
3
+ FlexMock is a flexible mocking library for use in unit testing and
4
+ behavior specification in Ruby. This release is a minor release with a
5
+ few bug fixes and some simple features.
6
+
7
+ == Changes in 1.3.0
8
+
9
+ === Features
10
+
11
+ * Add 'and' and 'on' modifiers for the RSpec spy matcher.
12
+
13
+ * Add 'and' and 'on' options to the assert_spy_called test method.
14
+
15
+ * General documentation improvement.
16
+
17
+ === Bug Fixes
18
+
19
+ * Fix bug in should_fail test helper that was not detecting failed
20
+ failures.
21
+
22
+ == Changes in 1.2.0
23
+
24
+ === Features
25
+
26
+ * Add auto configure by requiring 'flexmock/rspec/configure'.
27
+
28
+ == Changes in 1.1.0
29
+
30
+ === Features
31
+
32
+ * Add block support to pass_thru.
33
+
34
+ == Changes in 1.0.0
35
+
36
+ === Features
37
+
38
+ * Mocks may now have a base class that limits what methods may be
39
+ mocked. This allows early detection of outdated mock setups when the
40
+ methods in the class are refactored.
41
+
42
+ * Spy assertions are now allowed. The verification of the calling of
43
+ mocked methods may now be done in the "then" portion of the test,
44
+ after the code under test has been run. This allows for much more
45
+ natural Given/When/Then style testing.
46
+
47
+ * A custom assert method (assert_spy_called) has been added to make
48
+ spy assertions easy when using Test::Unit or MiniTest.
49
+
50
+ * An RSpec matcher (have_received) has been added to make spy
51
+ assertions easy when using RSpec.
52
+
53
+ === Bug Fixes
54
+
55
+ * Now correctly handling the mocking of meta-programmed methods.
56
+
57
+ * Using the documented +singleton_methods+ method.
58
+
59
+ * Accidently trying to partial mock a regular mock is now a no-op.
60
+
61
+ == What is FlexMock?
62
+
63
+ FlexMock is a flexible framework for creating mock object for testing.
64
+ When running unit tests, it is often desirable to use isolate the
65
+ objects being tested from the "real world" by having them interact
66
+ with simplified test objects. Sometimes these test objects simply
67
+ return values when called, other times they verify that certain
68
+ methods were called with particular arguments in a particular order.
69
+
70
+ FlexMock makes creating these test objects easy.
71
+
72
+ === Features
73
+
74
+ * Easy integration with both Test::Unit and RSpec. Mocks created with the
75
+ flexmock method are automatically verified at the end of the test or
76
+ example.
77
+
78
+ * A fluent interface that allows mock behavior to be specified very
79
+ easily.
80
+
81
+ * A "record mode" where an existing implementation can record its
82
+ interaction with a mock for later validation against a new
83
+ implementation.
84
+
85
+ * Easy mocking of individual methods in existing, non-mock objects.
86
+
87
+ * Easy mocking of chains of method calls.
88
+
89
+ * The ability to cause classes to instantiate test instances (instead of real
90
+ instances) for the duration of a test.
91
+
92
+ === Example
93
+
94
+ Suppose you had a Dog object that wagged a tail when it was happy.
95
+ Something like this:
96
+
97
+ class Dog
98
+ def initialize(a_tail)
99
+ @tail = a_tail
100
+ end
101
+ def happy
102
+ @tail.wag
103
+ end
104
+ end
105
+
106
+ To test the +Dog+ class without a real +Tail+ object (perhaps because
107
+ real +Tail+ objects activate servos in some robotic equipment), you
108
+ can do something like this:
109
+
110
+ RSpec.configure do |config|
111
+ config.mock_with :flexmock
112
+ end
113
+
114
+ describe Dog do
115
+ it "wags its tail when happy" do
116
+ tail = flexmock("tail")
117
+ tail.should_receive(:wag).once
118
+ dog = Dog.new(tail)
119
+ dog.happy
120
+ end
121
+ end
122
+
123
+ FlexMock will automatically verify that the mocked tail object received the
124
+ message +wag+ exactly one time. If it doesn't, the test will not pass.
125
+
126
+ Here's the same thing using the new spy support:
127
+
128
+ describe Dog do
129
+ it "wags its tail when happy" do
130
+ tail = flexmock("tail")
131
+ dog = Dog.new(tail)
132
+ dog.happy
133
+ tail.should have_received(:wag)
134
+ end
135
+ end
136
+
137
+ This style works particularly well with the rspec-given library.
138
+
139
+ require 'rspec/given'
140
+
141
+ describe Dog do
142
+ context "when the dog is happy" do
143
+ Given(:tail) { flexmock(:on, Tail) }
144
+ Given(:dog) { Dog.new(tail) }
145
+
146
+ When { dog.happy }
147
+
148
+ Then { tail.should have_received(:wag) }
149
+ end
150
+ end
151
+
152
+ See the FlexMock documentation at http://flexmock.rubyforge.org for details on
153
+ specifying arguments and return values on mocked methods, as well as a simple
154
+ technique for mocking tail objects when the Dog class creates the tail objects
155
+ directly.
156
+
157
+ == Availability
158
+
159
+ You can make sure you have the latest version with a quick RubyGems command:
160
+
161
+ gem install flexmock (you may need root/admin privileges)
162
+
163
+ You will find documentation at: http://flexmock.rubyforge.org.
164
+
165
+ -- Jim Weirich
data/lib/flexmock/core.rb CHANGED
@@ -99,12 +99,26 @@ class FlexMock
99
99
  self
100
100
  end
101
101
 
102
- CallRecord = Struct.new(:method_name, :args, :expectation)
102
+ CallRecord = Struct.new(:method_name, :args, :block_given, :expectation) do
103
+ def matches?(sym, actual_args, options)
104
+ method_name == sym &&
105
+ ArgumentMatching.all_match?(actual_args, args) &&
106
+ matches_block?(options[:with_block])
107
+ end
108
+
109
+ private
110
+
111
+ def matches_block?(block_option)
112
+ block_option.nil? ||
113
+ (block_option && block_given) ||
114
+ (!block_option && !block_given)
115
+ end
116
+ end
103
117
 
104
118
  # Handle missing methods by attempting to look up a handler.
105
119
  def method_missing(sym, *args, &block)
106
120
  enhanced_args = block_given? ? args + [block] : args
107
- call_record = CallRecord.new(sym, enhanced_args)
121
+ call_record = CallRecord.new(sym, enhanced_args, block_given?)
108
122
  @calls << call_record
109
123
  flexmock_wrap do
110
124
  if handler = @expectations[sym]
@@ -146,8 +160,15 @@ class FlexMock
146
160
  # True if the mock received the given method and arguments.
147
161
  def flexmock_received?(sym, args, options={})
148
162
  count = 0
163
+ additional = options[:and] || []
164
+ additional = [additional] if additional.is_a?(Proc)
149
165
  @calls.each { |call_record|
150
- count += 1 if (call_record.method_name == sym) && ArgumentMatching.all_match?(args, call_record.args)
166
+ if call_record.matches?(sym, args, options)
167
+ count += 1
168
+ if options[:on_count].nil? || count == options[:on_count]
169
+ additional.each do |add| add.call(*call_record.args) end
170
+ end
171
+ end
151
172
  }
152
173
  if options[:times]
153
174
  result = count == options[:times]
@@ -12,13 +12,12 @@ class FlexMock
12
12
  @block = nil
13
13
  @times = nil
14
14
  @needs_block = nil
15
+ @additional_validations = []
15
16
  end
16
17
 
17
18
  def matches?(spy)
18
19
  @spy = spy
19
- @options = {}
20
- @options[:times] = @times if @times
21
- @options[:with_block] = @needs_block unless @needs_block.nil?
20
+ @options = construct_options
22
21
  @spy.flexmock_received?(@method_name, @args, @options)
23
22
  end
24
23
 
@@ -61,6 +60,25 @@ class FlexMock
61
60
  def twice
62
61
  times(2)
63
62
  end
63
+
64
+ def on(on_count)
65
+ @on_count = on_count
66
+ self
67
+ end
68
+
69
+ def and(&block)
70
+ @additional_validations << block
71
+ self
72
+ end
73
+
74
+ def construct_options
75
+ {
76
+ :times => @times,
77
+ :with_block => @needs_block,
78
+ :on_count => @on_count,
79
+ :and => @additional_validations,
80
+ }
81
+ end
64
82
  end
65
83
 
66
84
  def have_received(method_name)
@@ -2,7 +2,7 @@ class FlexMock
2
2
  module Version
3
3
  NUMBERS = [
4
4
  MAJOR = 1,
5
- MINOR = 2,
5
+ MINOR = 3,
6
6
  BUILD = 0,
7
7
  ]
8
8
  end
@@ -22,23 +22,27 @@ describe "Dog" do
22
22
  dog.wags(:tail)
23
23
  end
24
24
 
25
- it "wags the tail" do
25
+ it "accepts no with" do
26
+ dog.should have_received(:wags)
27
+ end
28
+
29
+ it "accepts with restriction" do
26
30
  dog.should have_received(:wags).with(:tail)
27
31
  end
28
32
 
29
- it "does not bark the tail" do
33
+ it "accepts not methods called" do
30
34
  dog.should_not have_received(:bark)
31
35
  end
32
36
 
33
- it "rejects wag(:foot)" do
37
+ it "rejects incorrect with restriction" do
34
38
  should_fail(/^expected wag\(:foot\) to be received by <FlexMock:Dog Mock>/i) do
35
39
  dog.should have_received(:wag).with(:foot)
36
40
  end
37
41
  end
38
42
 
39
- it "rejects not wag(:tail)" do
40
- should_fail(/^expected wag\(:foot\) to be received by <FlexMock:Dog Mock>/i) do
41
- dog.should_not have_received(:wag).with(:tail)
43
+ it "rejects not on correct matcher" do
44
+ should_fail(/^expected wags\(:tail\) to NOT be received by <FlexMock:Dog Mock>/i) do
45
+ dog.should_not have_received(:wags).with(:tail)
42
46
  end
43
47
  end
44
48
  end
@@ -102,6 +106,65 @@ describe "Dog" do
102
106
  end
103
107
  end
104
108
 
109
+ context "with additional validations" do
110
+ it "accepts when correct" do
111
+ dog.wags(:tail)
112
+ dog.should have_received(:wags).and { |arg| arg.should == :tail }
113
+ end
114
+
115
+ it "rejects when incorrect" do
116
+ dog.wags(:tail)
117
+ should_fail(/expected: :foot.*got: :tail/im) do
118
+ dog.should have_received(:wags).and { |arg| arg.should == :foot }
119
+ end
120
+ end
121
+
122
+ it "rejects on all calls" do
123
+ dog.wags(:foot)
124
+ dog.wags(:foot)
125
+ dog.wags(:tail)
126
+ should_fail(/expected: :foot.*got: :tail/im) do
127
+ dog.should have_received(:wags).and { |arg| arg.should == :foot }
128
+ end
129
+ end
130
+
131
+ it "runs the first additional block" do
132
+ dog.wags(:tail)
133
+ should_fail(/expected: :foot.*got: :tail/im) do
134
+ dog.should have_received(:wags).and { |args|
135
+ args.should == :foot
136
+ }.and { |args|
137
+ args.should == :tail
138
+ }
139
+ end
140
+ end
141
+
142
+ it "runs the second additional block" do
143
+ dog.wags(:tail)
144
+ should_fail(/expected: :foot.*got: :tail/im) do
145
+ dog.should have_received(:wags).and { |args|
146
+ args.should == :tail
147
+ }.and { |args|
148
+ args.should == :foot
149
+ }
150
+ end
151
+ end
152
+ end
153
+
154
+ context "with ordinal constraints" do
155
+ it "detects the first call" do
156
+ dog.wags(:tail)
157
+ dog.wags(:foot)
158
+ dog.should have_received(:wags).and { |arg| arg.should == :tail }.on(1)
159
+ end
160
+
161
+ it "detects the second call" do
162
+ dog.wags(:tail)
163
+ dog.wags(:foot)
164
+ dog.should have_received(:wags).and { |arg| arg.should == :foot }.on(2)
165
+ end
166
+ end
167
+
105
168
  context "with blocks" do
106
169
  before do
107
170
  dog.wags { }
@@ -126,16 +189,19 @@ describe "Dog" do
126
189
 
127
190
  it "rejects barks with a block" do
128
191
  should_fail(/with a block/) do
129
- dog.should have_received(:wags).with_a_block
192
+ dog.should have_received(:barks).with_a_block
130
193
  end
131
194
  end
132
195
  end
133
196
 
134
197
  def should_fail(message_pattern)
198
+ failed = false
135
199
  begin
136
200
  yield
137
201
  rescue RSpec::Expectations::ExpectationNotMetError => ex
202
+ failed = true
138
203
  ex.message.should match(message_pattern)
139
204
  end
205
+ RSpec::Expectations.fail_with "Expected block to fail with message #{message_pattern.inspect}, no failure detected" unless failed
140
206
  end
141
207
  end
data/test/spys_test.rb CHANGED
@@ -90,6 +90,59 @@ class TestSpys < Test::Unit::TestCase
90
90
  assert_spy_called @spy, :foo, Proc
91
91
  end
92
92
 
93
+ def test_spy_accepts_correct_additional_validations
94
+ @spy.foo(2)
95
+ is_even = proc { |n| assert_equal 0, n%2 }
96
+ assert_spy_called @spy, { and: is_even }, :foo, Integer
97
+ end
98
+
99
+ def test_spy_accepts_multiple_additional_validations_first_failing
100
+ @spy.foo(4)
101
+ is_two = proc { |n| assert_equal 2, n }
102
+ is_even = proc { |n| assert_equal 0, n%2 }
103
+ assert_failed(/2.*expected but was.*4/mi) do
104
+ assert_spy_called @spy, { and: [is_two, is_even] }, :foo, Integer
105
+ end
106
+ end
107
+
108
+ def test_spy_accepts_multiple_additional_validations_second_failing
109
+ @spy.foo(4)
110
+ is_even = proc { |n| assert_equal 0, n%2 }
111
+ is_two = proc { |n| assert_equal 2, n }
112
+ assert_failed(/2.*expected but was.*4/mi) do
113
+ assert_spy_called @spy, { and: [is_even, is_two] }, :foo, Integer
114
+ end
115
+ end
116
+
117
+ def test_spy_rejects_incorrect_additional_validations
118
+ @spy.foo(3)
119
+ is_even = proc { |n| assert_equal 0, n%2 }
120
+ assert_failed(/0.*expected but was.*1/mi) do
121
+ assert_spy_called @spy, { and: is_even }, :foo, Integer
122
+ end
123
+ end
124
+
125
+ def test_spy_selectively_applies_additional_validations
126
+ @spy.foo(2)
127
+ @spy.foo(3)
128
+ @spy.foo(4)
129
+ is_even = proc { |n| assert_equal 0, n%2 }
130
+ assert_failed(/0.*expected but was.*1/mi) do
131
+ assert_spy_called @spy, { and: is_even, on: 2 }, :foo, Integer
132
+ end
133
+ end
134
+
135
+ def assert_failed(message_pattern)
136
+ failed = false
137
+ begin
138
+ yield
139
+ rescue MiniTest::Assertion => ex
140
+ failed = true
141
+ assert_match message_pattern, ex.message
142
+ end
143
+ refute(!failed, "Expected block to fail")
144
+ end
145
+
93
146
  def test_spy_methods_can_be_stubbed
94
147
  @spy.should_receive(:foo).and_return(:hi)
95
148
  result = @spy.foo
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flexmock
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-15 00:00:00.000000000 Z
12
+ date: 2013-01-27 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! "\n FlexMock is a extremely simple mock object class compatible\n
15
15
  \ with the Test::Unit framework. Although the FlexMock's\n interface is
@@ -18,7 +18,7 @@ email: jim.weirich@gmail.com
18
18
  executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files:
21
- - README.rdoc
21
+ - doc/index.rdoc
22
22
  - CHANGES
23
23
  - doc/GoogleExample.rdoc
24
24
  - doc/releases/flexmock-0.4.0.rdoc
@@ -42,6 +42,10 @@ extra_rdoc_files:
42
42
  - doc/releases/flexmock-0.9.0.rdoc
43
43
  - doc/releases/flexmock-1.0.0.rdoc
44
44
  - doc/releases/flexmock-1.0.3.rdoc
45
+ - doc/releases/flexmock-1.0.4.rdoc
46
+ - doc/releases/flexmock-1.1.0.rdoc
47
+ - doc/releases/flexmock-1.2.0.rdoc
48
+ - doc/releases/flexmock-1.3.0.rdoc
45
49
  - doc/examples/rspec_examples_spec.rdoc
46
50
  - doc/examples/test_unit_examples_test.rdoc
47
51
  files:
@@ -49,7 +53,7 @@ files:
49
53
  - Gemfile
50
54
  - Gemfile.lock
51
55
  - Rakefile
52
- - README.rdoc
56
+ - README.md
53
57
  - TAGS
54
58
  - lib/flexmock/argument_matchers.rb
55
59
  - lib/flexmock/argument_matching.rb
@@ -117,6 +121,7 @@ files:
117
121
  - test/undefined_test.rb
118
122
  - flexmock.blurb
119
123
  - install.rb
124
+ - doc/index.rdoc
120
125
  - doc/GoogleExample.rdoc
121
126
  - doc/releases/flexmock-0.4.0.rdoc
122
127
  - doc/releases/flexmock-0.4.1.rdoc
@@ -139,6 +144,10 @@ files:
139
144
  - doc/releases/flexmock-0.9.0.rdoc
140
145
  - doc/releases/flexmock-1.0.0.rdoc
141
146
  - doc/releases/flexmock-1.0.3.rdoc
147
+ - doc/releases/flexmock-1.0.4.rdoc
148
+ - doc/releases/flexmock-1.1.0.rdoc
149
+ - doc/releases/flexmock-1.2.0.rdoc
150
+ - doc/releases/flexmock-1.3.0.rdoc
142
151
  - doc/examples/rspec_examples_spec.rdoc
143
152
  - doc/examples/test_unit_examples_test.rdoc
144
153
  homepage: https://github.com/jimweirich/flexmock
@@ -160,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
169
  version: '0'
161
170
  segments:
162
171
  - 0
163
- hash: -972765428365068748
172
+ hash: -2506978520909989178
164
173
  required_rubygems_version: !ruby/object:Gem::Requirement
165
174
  none: false
166
175
  requirements: