rspec_candy 0.1.2 → 0.2.0

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