rspec_candy 0.1.2 → 0.2.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/README.md CHANGED
@@ -1,15 +1,51 @@
1
1
  #rspec_candy
2
2
 
3
3
 
4
- A collection of nifty helpers for your rspec suite.
4
+ A collection of nifty helpers and matchers for your RSpec suite.
5
5
 
6
- Will work for RSpec1 and Rails 2, or RSpec2 and Rails 3, or no Rails at all..
6
+ Will work for RSpec1 and Rails 2, or RSpec2 and Rails 3, or no Rails at all.
7
7
 
8
8
 
9
- ##Usage
9
+ ##Installation
10
10
 
11
11
  Add `rspec_candy` to your Gemfile.
12
- Add `require 'rspec_candy/helpers'` to your spec_helper.rb, after the rspec requires.
12
+
13
+ Now, in your `spec_helper.rb`, add this after your RSpec requires:
14
+
15
+ require 'rspec_candy/all'
16
+
17
+ If you only care about the matchers or helpers you can also more specifically require:
18
+
19
+ require 'rspec_candy/matchers'
20
+ require 'rspec_candy/helpers'
21
+
22
+
23
+ ##Matchers provided
24
+
25
+ **be_same_number_as**
26
+
27
+ Tests if the given number is the "same" as the receiving number, regardless of whether you're comparing `Fixnums` (integers), `Floats` and `BigDecimals`:
28
+
29
+ 100.should be_same_number_as(100.0)
30
+ 50.4.should be_same_number_as(BigDecimal('50.4'))
31
+
32
+ Note that "same" means "same for your purposes". Internally the matcher compares normalized results of `#to_s`.
33
+
34
+ **be_same_second_as**
35
+
36
+ Tests if the given `Time` or `DateTime` is the same as the receiving `Time` or `DateTime`, [ignoring sub-second differences](https://makandracards.com/makandra/1057-why-two-ruby-time-objects-are-not-equal-although-they-appear-to-be):
37
+
38
+ Time.parse('2012-12-01 14:00:00.5').should == Time.parse('2012-12-01 14:00')
39
+
40
+ Note that two times in a different time zones will still be considered different.q
41
+
42
+ **include_hash**
43
+
44
+ Matches if the given hash is included in the receiving hash:
45
+
46
+ { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a') # passes
47
+ { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'b') # fails
48
+ { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a', :baz => 'c') # fails
13
49
 
14
50
 
15
51
  ##Helpers provided
@@ -0,0 +1,2 @@
1
+ require 'rspec_candy/helpers'
2
+ require 'rspec_candy/matchers'
@@ -0,0 +1,2 @@
1
+ require 'rspec_candy/version'
2
+ require 'rspec_candy/switcher'
@@ -1,5 +1,4 @@
1
- require 'rspec_candy/version'
2
- require 'rspec_candy/switcher'
1
+ require 'rspec_candy/base'
3
2
  require 'rspec_candy/helpers/disposable_copy'
4
3
  require 'rspec_candy/helpers/it_should_act_like'
5
4
  require 'rspec_candy/helpers/new_with_stubs'
@@ -0,0 +1,14 @@
1
+ RSpecCandy::Switcher.define_matcher :be_same_number_as do |expected|
2
+
3
+ match do |actual|
4
+ actual.is_a?(Numeric) && expected.is_a?(Numeric) && normalize(expected) == normalize(actual)
5
+ end
6
+
7
+ def normalize(number)
8
+ number.to_s.tap do |str|
9
+ str << ".0" unless str.include?('.')
10
+ end
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,9 @@
1
+ RSpecCandy::Switcher.define_matcher :be_same_second_as do |expected|
2
+
3
+ match do |actual|
4
+ actual.to_i == expected.to_i
5
+ end
6
+
7
+
8
+ end
9
+
@@ -0,0 +1,7 @@
1
+ RSpecCandy::Switcher.define_matcher :include_hash do |expected|
2
+
3
+ match do |actual|
4
+ !actual.nil? && actual.slice(*expected.keys) == expected
5
+ end
6
+
7
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec_candy/base'
2
+ require 'rspec_candy/matchers/include_hash'
3
+ require 'rspec_candy/matchers/be_same_number_as'
4
+ require 'rspec_candy/matchers/be_same_second_as'
@@ -1,3 +1,3 @@
1
1
  module RSpecCandy
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/rspec_candy.rb CHANGED
@@ -1,2 +1,2 @@
1
- # empty, so Bundler does not require anything
2
- # put a "require rspec_candy/helpers" into your spec_helper
1
+ # empty so bundler doesn't require a thing
2
+
@@ -8,7 +8,7 @@ require "#{File.dirname(__FILE__)}/app_root/config/environment"
8
8
  require 'spec/rails'
9
9
 
10
10
  # Load dependencies
11
- require 'rspec_candy/helpers'
11
+ require 'rspec_candy/all'
12
12
 
13
13
  # Require support code
14
14
  Dir["../shared/support/**/*.rb"].each {|f| require f}
@@ -9,7 +9,7 @@ require "#{File.dirname(__FILE__)}/app_root/config/environment"
9
9
  require 'rspec/rails'
10
10
 
11
11
  # Load dependencies
12
- require 'rspec_candy/helpers'
12
+ require 'rspec_candy/all'
13
13
 
14
14
  # Require support code
15
15
  Dir["../shared/support/**/*.rb"].each {|f| require f}
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpecCandy do
4
+ describe 'matchers' do
5
+ describe 'be_same_number_as' do
6
+
7
+ it 'should correctly compare Fixnums and Floats' do
8
+ 1.should be_same_number_as(1.0)
9
+ 2.should_not be_same_number_as(1.0)
10
+ -1.should be_same_number_as(-1.0)
11
+ -2.should_not be_same_number_as(-1.0)
12
+ end
13
+
14
+ it 'should correctly compare Fixnums and BigDecimals' do
15
+ 1.should be_same_number_as(BigDecimal('1.0'))
16
+ 2.should_not be_same_number_as(BigDecimal('1.0'))
17
+ -1.should be_same_number_as(BigDecimal('-1.0'))
18
+ -2.should_not be_same_number_as(BigDecimal('-1.0'))
19
+ end
20
+
21
+ it 'should correctly compare Floats and BigDecimals' do
22
+ 1.1.should be_same_number_as(BigDecimal('1.1'))
23
+ 2.1.should_not be_same_number_as(BigDecimal('1.1'))
24
+ -1.1.should be_same_number_as(BigDecimal('-1.1'))
25
+ -2.1.should_not be_same_number_as(BigDecimal('-1.1'))
26
+ end
27
+
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpecCandy do
4
+ describe 'matchers' do
5
+ describe 'be_same_second_as' do
6
+
7
+ it 'should consider equal two Times with the same second' do
8
+ Time.parse('2012-05-01 14:15:16').should be_same_second_as(Time.parse('2012-05-01 14:15:16'))
9
+ Time.parse('2012-05-01 14:15:17').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16'))
10
+ end
11
+
12
+ it 'should ignore sub-second differences' do
13
+ Time.parse('2012-05-01 00:00:00.1').should be_same_second_as(Time.parse('2012-05-01 00:00:00.2'))
14
+ end
15
+
16
+ it 'should correctly compare Time and DateTime objects' do
17
+ Time.parse('2012-05-01 14:15:16 +0000').should be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000'))
18
+ Time.parse('2012-05-01 14:15:17 +0000').should_not be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000'))
19
+ Time.parse('2012-05-01 00:00:00.1 +0000').should be_same_second_as(DateTime.parse('2012-05-01 00:00:00.2 +0000'))
20
+ end
21
+
22
+ it 'should consider different two times in different zones' do
23
+ Time.parse('2012-05-01 14:15:16 +0000').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16 +0100'))
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpecCandy do
4
+ describe 'matchers' do
5
+ describe 'include_hash' do
6
+
7
+ it 'should match if all pairs in the given hash exist in the receiver' do
8
+ { :foo => 'a', :bar => 'b' , :bam => 'c'}.should include_hash(:foo => 'a', :bam => 'c')
9
+ end
10
+
11
+ it 'should not match if the given hash has keys that do not exist in the receiver' do
12
+ { :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :baz => 'd')
13
+ end
14
+
15
+ it 'should not match if the given hash has values that are different in the receiver' do
16
+ { :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :bam => 'different-value')
17
+ end
18
+
19
+ it 'should not crash and not match if the receiver is nil' do
20
+ nil.should_not include_hash(:foo => 'a')
21
+ end
22
+
23
+ it 'should match if the receiver is empty and the given hash is empty' do
24
+ {}.should include_hash({})
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_candy
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
8
  - 2
10
- version: 0.1.2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Henning Koch
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-07-05 00:00:00 +02:00
19
+ date: 2012-08-06 00:00:00 +02:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,8 @@ files:
46
46
  - README.md
47
47
  - Rakefile
48
48
  - lib/rspec_candy.rb
49
+ - lib/rspec_candy/all.rb
50
+ - lib/rspec_candy/base.rb
49
51
  - lib/rspec_candy/helpers.rb
50
52
  - lib/rspec_candy/helpers/disposable_copy.rb
51
53
  - lib/rspec_candy/helpers/it_should_act_like.rb
@@ -58,6 +60,10 @@ files:
58
60
  - lib/rspec_candy/helpers/should_receive_chain.rb
59
61
  - lib/rspec_candy/helpers/stub_any_instance.rb
60
62
  - lib/rspec_candy/helpers/stub_existing.rb
63
+ - lib/rspec_candy/matchers.rb
64
+ - lib/rspec_candy/matchers/be_same_number_as.rb
65
+ - lib/rspec_candy/matchers/be_same_second_as.rb
66
+ - lib/rspec_candy/matchers/include_hash.rb
61
67
  - lib/rspec_candy/switcher.rb
62
68
  - lib/rspec_candy/version.rb
63
69
  - rspec_candy.gemspec
@@ -119,10 +125,11 @@ files:
119
125
  - spec/shared/rspec_candy/helpers/should_receive_chain_spec.rb
120
126
  - spec/shared/rspec_candy/helpers/stub_any_instance_spec.rb
121
127
  - spec/shared/rspec_candy/helpers/stub_existing_spec.rb
128
+ - spec/shared/rspec_candy/matchers/be_same_number_as_spec.rb
129
+ - spec/shared/rspec_candy/matchers/be_same_second_as_spec.rb
130
+ - spec/shared/rspec_candy/matchers/include_hash_spec.rb
122
131
  - spec/shared/support/matchers/pass_as_describe_block.rb
123
132
  - spec/shared/support/matchers/pass_as_example.rb
124
- - template/spec_candy.rails2.rb
125
- - template/spec_candy.rails3.rb
126
133
  has_rdoc: true
127
134
  homepage: https://github.com/makandra/rspec_candy
128
135
  licenses: []
@@ -1,171 +0,0 @@
1
- Object.class_eval do
2
-
3
- def should_receive_chain(*parts)
4
- setup_expectation_chain(parts)
5
- end
6
-
7
- def self.new_with_stubs(attrs)
8
- new.tap do |obj|
9
- obj.stub_existing attrs
10
- end
11
- end
12
-
13
- def stub_existing(attrs)
14
- attrs.each do |method, value|
15
- if respond_to?(method, true)
16
- stub(method => value)
17
- else
18
- raise "Attempted to stub non-existing method ##{method} on a #{self.class.name}"
19
- end
20
- end
21
- end
22
-
23
- def should_receive_and_return(methods_and_values)
24
- methods_and_values.each do |method, value|
25
- should_receive(method).and_return(value)
26
- end
27
- end
28
-
29
- def should_receive_all_with(methods_and_values)
30
- methods_and_values.each do |method, value|
31
- should_receive(method).with(value)
32
- end
33
- end
34
-
35
- def should_not_receive_and_execute(method)
36
- should_receive_and_execute(method, true)
37
- end
38
-
39
- def should_receive_and_execute(method, negate = false)
40
- method_base = method.to_s.gsub(/([\?\!\=\[\]]+)$/, '')
41
- method_suffix = $1
42
-
43
- method_called = "_#{method_base}_called#{method_suffix}"
44
- method_with_spy = "#{method_base}_with_spy#{method_suffix}"
45
- method_without_spy = "#{method_base}_without_spy#{method_suffix}"
46
-
47
- prototype = respond_to?(:singleton_class) ? singleton_class : metaclass
48
- prototype.class_eval do
49
-
50
- unless method_defined?(method_with_spy)
51
-
52
- define_method method_called do
53
- end
54
-
55
- define_method method_with_spy do |*args, &block|
56
- send(method_called, *args)
57
- send(method_without_spy, *args, &block)
58
- end
59
- alias_method_chain method, :spy
60
- end
61
-
62
- end
63
-
64
- expectation = negate ? :should_not_receive : :should_receive
65
- send(expectation, method_called)
66
- end
67
-
68
- private
69
-
70
- def setup_expectation_chain(parts)
71
- obj = self
72
- for part in parts
73
- if part == parts.last
74
- obj = add_expectation_chain_link(obj, part)
75
- else
76
- next_obj = Spec::Mocks::Mock.new('chain link')
77
- add_expectation_chain_link(obj, part).at_least(:once).and_return(next_obj)
78
- obj = next_obj
79
- end
80
- end
81
- obj
82
- end
83
-
84
- def add_expectation_chain_link(obj, part)
85
- if part.is_a?(Array)
86
- obj.should_receive(part.first).with(*part[1..-1])
87
- else
88
- obj.should_receive(part)
89
- end
90
- end
91
-
92
- end
93
-
94
- Spec::Example::ExampleGroupMethods.class_eval do
95
-
96
- # Improves it_should_behave_like in some ways:
97
- # - It scopes the reused examples so #let und #subject does not bleed into the reusing example groups
98
- # - It allows to parametrize the reused example group by appending a hash argument.
99
- # Every key/value pair in the hash will become a #let variable for the reused example group
100
- # - You can call it with a block. It will be available to the reused example group as let(:block)
101
- def it_should_act_like(shared_example_group, environment = {}, &block)
102
- description = "as #{shared_example_group}"
103
- description << " (#{environment.inspect})" if environment.present?
104
- describe description do
105
- environment.each do |name, value|
106
- let(name) { value }
107
- end
108
- let(:block) { block } if block
109
- it_should_behave_like(shared_example_group)
110
- end
111
- end
112
-
113
- end
114
-
115
- Spec::Rails::Example::ModelExampleGroup.class_eval do
116
-
117
- def self.it_should_run_callbacks_in_order(*callbacks)
118
- callbacks.push(:ordered => true)
119
- it_should_run_callbacks(*callbacks)
120
- end
121
-
122
- def self.it_should_run_callbacks(*callbacks)
123
- options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
124
- reason = callbacks.pop if callbacks.last.is_a?(String)
125
- hook = description_parts.last.sub(/^#/, '')
126
- should = ['should run callbacks', callbacks.inspect, ('in order' if options[:ordered]), reason].compact.join ' '
127
- send(:it, should) do
128
- callbacks.each do |callback|
129
- expectation = subject.should_receive(callback).once
130
- expectation.ordered if options[:ordered]
131
- end
132
- run_state_machine_callbacks_from_prose(hook) || subject.run_callbacks(hook)
133
- end
134
- end
135
-
136
- private
137
-
138
- def run_state_machine_callbacks_from_prose(prose)
139
- if parts = prose.match(/^(\w+) from ([\:\w]+) to ([\:\w]+)$/)
140
- name = parts[1].to_sym
141
- from = parts[2].sub(/^:/, '').to_sym
142
- to = parts[3].sub(/^:/, '').to_sym
143
- transition = StateMachine::Transition.new(subject, subject.class.state_machine, name, from, to)
144
- transition.run_callbacks
145
- end
146
- end
147
-
148
- end
149
-
150
- ActiveRecord::Base.class_eval do
151
-
152
- # Prevents the databse from being touched, but still runs all validations.
153
- def keep_invalid!
154
- errors.stub :empty? => false
155
- end
156
-
157
- end
158
-
159
- Class.class_eval do
160
-
161
- define_method :stub_any_instance do |stubs|
162
- unstubbed_new = method(:new)
163
- stub(:new).and_return do |*args|
164
- unstubbed_new.call(*args).tap do |obj|
165
- obj.stub stubs
166
- end
167
- end
168
- stubs
169
- end
170
-
171
- end
@@ -1,175 +0,0 @@
1
- Object.class_eval do
2
-
3
- # def should_receive_chain(*parts)
4
- # setup_expectation_chain(parts)
5
- # end
6
- #
7
- # def should_not_receive_chain(*parts)
8
- # setup_expectation_chain(parts, :negate => true)
9
- # end
10
- #
11
- # def self.new_with_stubs(attrs)
12
- # new.tap { |obj| obj.stub(attrs) }
13
- # end
14
- #
15
- # def should_receive_and_return(methods_and_values)
16
- # methods_and_values.each do |method, value|
17
- # should_receive(method).and_return(value)
18
- # end
19
- # end
20
- #
21
- # def self.new_and_store(options = {})
22
- # new.tap do |record|
23
- # record.send(:attributes=, options, false)
24
- # record.save!(:validate => false)
25
- # end
26
- # end
27
- #
28
- # def stub_existing(attrs)
29
- # attrs.each do |method, value|
30
- # if respond_to?(method, true)
31
- # stub(method => value)
32
- # else
33
- # raise "Attempted to stub non-existing method ##{method} on a #{self.class.name}"
34
- # end
35
- # end
36
- # end
37
- #
38
- # def should_receive_and_execute(method, negate = false)
39
- # method_base = method.to_s.gsub(/([\?\!\=\[\]]+)$/, '')
40
- # method_suffix = $1
41
- #
42
- # method_called = "_#{method_base}_called#{method_suffix}"
43
- # method_with_spy = "#{method_base}_with_spy#{method_suffix}"
44
- # method_without_spy = "#{method_base}_without_spy#{method_suffix}"
45
- #
46
- # prototype = respond_to?(:singleton_class) ? singleton_class : metaclass
47
- # prototype.class_eval do
48
- #
49
- # unless method_defined?(method_with_spy)
50
- #
51
- # define_method method_called do
52
- # end
53
- #
54
- # define_method method_with_spy do |*args, &block|
55
- # send(method_called, *args)
56
- # send(method_without_spy, *args, &block)
57
- # end
58
- # alias_method_chain method, :spy
59
- # end
60
- #
61
- # end
62
- #
63
- # expectation = negate ? :should_not_receive : :should_receive
64
- # send(expectation, method_called)
65
- # end
66
- #
67
- private
68
-
69
- def setup_expectation_chain(parts, options = {})
70
- obj = self
71
- for part in parts
72
- if part == parts.last
73
- expectation = options[:negate] ? :should_not_receive : :should_receive
74
- obj = add_expectation_chain_link(obj, expectation, part)
75
- else
76
- next_obj = RSpec::Mocks::Mock.new('chain link')
77
- add_expectation_chain_link(obj, :stub, part).and_return(next_obj)
78
- obj = next_obj
79
- end
80
- end
81
- obj
82
- end
83
-
84
- def add_expectation_chain_link(obj, expectation, part)
85
- if part.is_a?(Array)
86
- obj.send(expectation, part.first).with(*part[1..-1])
87
- else
88
- obj.send(expectation, part)
89
- end
90
- end
91
-
92
- end
93
-
94
- RSpec::Core::ExampleGroup.class_eval do
95
-
96
- def self.it_should_run_callbacks_in_order(*callbacks)
97
- callbacks.push(:ordered => true)
98
- it_should_run_callbacks(*callbacks)
99
- end
100
-
101
-
102
- # Improves it_should_behave_like in some ways:
103
- # - It scopes the reused examples so #let und #subject does not bleed into the reusing example groups
104
- # - It allows to parametrize the reused example group by appending a hash argument.
105
- # Every key/value pair in the hash will become a #let variable for the reused example group
106
- # - You can call it with a block. It will be available to the reused example group as let(:block)
107
- def self.it_should_act_like(shared_example_group, environment = {}, &block)
108
- description = "as #{shared_example_group}"
109
- description << " (#{environment.inspect})" if environment.present?
110
- describe description do
111
- environment.each do |name, value|
112
- let(name) { value }
113
- end
114
- let(:block) { block } if block
115
- it_should_behave_like(shared_example_group)
116
- end
117
- end
118
-
119
- def self.it_should_run_callbacks(*callbacks)
120
- options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
121
- reason = callbacks.pop if callbacks.last.is_a?(String)
122
- example_description = description
123
- send(:it, ["should run callbacks", callbacks.inspect, ('in order' if options[:ordered]), reason].compact.join(' ')) do
124
- # Set expectations
125
- callbacks.each do |callback|
126
- expectation = subject.should_receive(callback).once
127
- expectation.ordered if options[:ordered]
128
- end
129
- run_state_machine_callbacks_from_prose(example_description) || run_active_record_callbacks_from_prose(example_description)
130
- end
131
- end
132
-
133
- def run_active_record_callbacks_from_prose(prose)
134
- hook = prose.split.last.sub(/^#/, '')
135
- if hook.sub!(/_on_(create|update)$/, '')
136
- condition = "validation_context == :#{$1}"
137
- else
138
- condition = nil
139
- end
140
- if hook == 'validate'
141
- kind = 'before'
142
- action = 'validate'
143
- else
144
- hook =~ /^(before|after|around)_(.*)$/
145
- kind = $1
146
- action = $2
147
- end
148
- # Run all matching callbacks
149
- subject.send("_#{action}_callbacks").each do |callback|
150
- if callback.kind.to_s == kind && (condition.nil? || callback.options[:if].include?(condition))
151
- subject.send callback.filter.to_s.gsub(/[\(,\)]/, '').to_sym
152
- end
153
- end
154
- end
155
-
156
- def run_state_machine_callbacks_from_prose(prose)
157
- if parts = prose.match(/^(\#\w+) from ([\:\w]+) to ([\:\w]+)$/)
158
- name = parts[1].sub(/^#/, '').to_sym
159
- from = parts[2].sub(/^:/, '').to_sym
160
- to = parts[3].sub(/^:/, '').to_sym
161
- transition = StateMachine::Transition.new(subject, subject.class.state_machine, name, from, to)
162
- transition.run_callbacks
163
- end
164
- end
165
-
166
- end
167
-
168
- ActiveRecord::Base.class_eval do
169
-
170
- # Prevents the database from being touched, but still runs all validations.
171
- def keep_invalid!
172
- errors.stub :empty? => false
173
- end
174
-
175
- end