flexmock 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: