remockable 0.1.1 → 0.1.2
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 +6 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/LICENSE +1 -1
- data/README.markdown +50 -36
- data/Rakefile +1 -0
- data/lib/remockable/active_model/helpers.rb +2 -0
- data/lib/remockable/active_record/helpers.rb +2 -0
- data/lib/remockable/version.rb +1 -1
- data/remockable.gemspec +23 -0
- data/spec/active_model/allow_mass_assignment_of_spec.rb +86 -0
- data/spec/active_model/allow_values_for_spec.rb +46 -0
- data/spec/active_model/validate_acceptance_of_spec.rb +16 -0
- data/spec/active_model/validate_confirmation_of_spec.rb +14 -0
- data/spec/active_model/validate_exclusion_of_spec.rb +16 -0
- data/spec/active_model/validate_format_of_spec.rb +18 -0
- data/spec/active_model/validate_inclusion_of_spec.rb +16 -0
- data/spec/active_model/validate_length_of_spec.rb +26 -0
- data/spec/active_model/validate_numericality_of_spec.rb +23 -0
- data/spec/active_model/validate_presence_of_spec.rb +14 -0
- data/spec/active_record/accept_nested_attributes_for_spec.rb +47 -0
- data/spec/active_record/belong_to_spec.rb +62 -0
- data/spec/active_record/have_and_belong_to_many_spec.rb +67 -0
- data/spec/active_record/have_column_spec.rb +109 -0
- data/spec/active_record/have_default_scope_spec.rb +169 -0
- data/spec/active_record/have_index_spec.rb +89 -0
- data/spec/active_record/have_many_spec.rb +70 -0
- data/spec/active_record/have_one_spec.rb +61 -0
- data/spec/active_record/have_scope_spec.rb +229 -0
- data/spec/active_record/validate_associated_spec.rb +27 -0
- data/spec/active_record/validate_uniqueness_of_spec.rb +23 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/active_record_example_group.rb +22 -0
- data/spec/support/class_builder.rb +39 -0
- data/spec/support/shared/a_validation_matcher.rb +130 -0
- data/spec/support/shared/an_active_record_matcher.rb +53 -0
- metadata +134 -40
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :have_scope do
|
4
|
+
let(:options) { :one }
|
5
|
+
|
6
|
+
it_behaves_like 'an Active Record matcher' do
|
7
|
+
let(:model) { build_class(:User, ActiveRecord::Base) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
create_table(:users) { |table| table.string(:one) }
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { model.new }
|
14
|
+
|
15
|
+
context 'description' do
|
16
|
+
let(:matcher) { send(matcher_name, *options) }
|
17
|
+
|
18
|
+
it 'has a custom description' do
|
19
|
+
name = matcher.instance_variable_get(:@name).to_s.gsub(/_/, ' ')
|
20
|
+
with = " with #{matcher.expected}" if matcher.expected.any?
|
21
|
+
|
22
|
+
matcher.description.should == "have scope #{name}#{with}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a scope name' do
|
27
|
+
it 'matches if the scope exists' do
|
28
|
+
model.instance_eval { scope(:no_one, where(:one => nil)) }
|
29
|
+
model.should have_scope(:no_one)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not match if the scope does not exist' do
|
33
|
+
model.should_not have_scope(:no_one)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with an eager_load value' do
|
38
|
+
it 'matches if the scope exists and the query matches' do
|
39
|
+
model.instance_eval { scope(:loaded, eager_load(:order)) }
|
40
|
+
model.should have_scope(:loaded).eager_load(:order)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not match if the query does not match' do
|
44
|
+
model.instance_eval { scope(:loaded, eager_load(:order)) }
|
45
|
+
model.should_not have_scope(:loaded).eager_load(:from)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with a from value' do
|
50
|
+
it 'matches if the scope exists and the query matches' do
|
51
|
+
model.instance_eval { scope(:used, from('users')) }
|
52
|
+
model.should have_scope(:used).from('users')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does not match if the query does not match' do
|
56
|
+
model.instance_eval { scope(:used, from('users')) }
|
57
|
+
model.should_not have_scope(:used).from('friends')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with a group value' do
|
62
|
+
it 'matches if the scope exists and the query matches' do
|
63
|
+
model.instance_eval { scope(:grouped, group('one')) }
|
64
|
+
model.should have_scope(:grouped).group('one')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not match if the query does not match' do
|
68
|
+
model.instance_eval { scope(:grouped, group('one')) }
|
69
|
+
model.should_not have_scope(:grouped).group('two')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with a having value' do
|
74
|
+
it 'matches if the scope exists and the query matches' do
|
75
|
+
model.instance_eval { scope(:had, having('COUNT(*) > 1')) }
|
76
|
+
model.should have_scope(:had).having('COUNT(*) > 1')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'does not match if the query does not match' do
|
80
|
+
model.instance_eval { scope(:had, having('COUNT(*) > 1')) }
|
81
|
+
model.should_not have_scope(:had).having('COUNT(*) > 2')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with an includes value' do
|
86
|
+
it 'matches if the scope exists and the query matches' do
|
87
|
+
model.instance_eval { scope(:joined, includes(:company)) }
|
88
|
+
model.should have_scope(:joined).includes(:company)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'does not match if the query does not match' do
|
92
|
+
model.instance_eval { scope(:joined, includes(:company)) }
|
93
|
+
model.should_not have_scope(:joined).includes(:address)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with a joins value' do
|
98
|
+
it 'matches if the scope exists and the query matches' do
|
99
|
+
model.instance_eval { scope(:joined, joins('INNER JOIN friends')) }
|
100
|
+
model.should have_scope(:joined).joins('INNER JOIN friends')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'does not match if the query does not match' do
|
104
|
+
model.instance_eval { scope(:joined, joins('INNER JOIN friends')) }
|
105
|
+
model.should_not have_scope(:joined).joins('INNER JOIN enemies')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'with a limit value' do
|
110
|
+
it 'matches if the scope exists and the query matches' do
|
111
|
+
model.instance_eval { scope(:limited, limit(10)) }
|
112
|
+
model.should have_scope(:limited).limit(10)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'does not match if the query does not match' do
|
116
|
+
model.instance_eval { scope(:limited, limit(10)) }
|
117
|
+
model.should_not have_scope(:limited).limit(15)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'with a lock value' do
|
122
|
+
it 'matches if the scope exists and the query matches' do
|
123
|
+
model.instance_eval { scope(:locked, lock(true)) }
|
124
|
+
model.should have_scope(:locked).lock(true)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'does not match if the query does not match' do
|
128
|
+
model.instance_eval { scope(:locked, lock(true)) }
|
129
|
+
model.should_not have_scope(:locked).lock(false)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'with an offset value' do
|
134
|
+
it 'matches if the scope exists and the query matches' do
|
135
|
+
model.instance_eval { scope(:shifted, offset(10)) }
|
136
|
+
model.should have_scope(:shifted).offset(10)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'does not match if the query does not match' do
|
140
|
+
model.instance_eval { scope(:shifted, offset(10)) }
|
141
|
+
model.should_not have_scope(:shifted).offset(15)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with an order value' do
|
146
|
+
it 'matches if the scope exists and the query matches' do
|
147
|
+
model.instance_eval { scope(:ordered, order('one')) }
|
148
|
+
model.should have_scope(:ordered).order('one')
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'does not match if the query does not match' do
|
152
|
+
model.instance_eval { scope(:ordered, order('one')) }
|
153
|
+
model.should_not have_scope(:ordered).order('id')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'with a preload value' do
|
158
|
+
it 'matches if the scope exists and the query matches' do
|
159
|
+
model.instance_eval { scope(:preloaded, preload(:company)) }
|
160
|
+
model.should have_scope(:preloaded).preload(:company)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'does not match if the query does not match' do
|
164
|
+
model.instance_eval { scope(:preloaded, preload(:company)) }
|
165
|
+
model.should_not have_scope(:preloaded).preload(:address)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'with a readonly value' do
|
170
|
+
it 'matches if the scope exists and the query matches' do
|
171
|
+
model.instance_eval { scope(:prepared, readonly(false)) }
|
172
|
+
model.should have_scope(:prepared).readonly(false)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'does not match if the query does not match' do
|
176
|
+
model.instance_eval { scope(:prepared, readonly(false)) }
|
177
|
+
model.should_not have_scope(:prepared).readonly(true)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'with a reorder value' do
|
182
|
+
it 'matches if the scope exists and the query matches' do
|
183
|
+
model.instance_eval { scope(:reordered, reorder('one')) }
|
184
|
+
model.should have_scope(:reordered).reorder('one')
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'does not match if the query does not match' do
|
188
|
+
model.instance_eval { scope(:reordered, reorder('one')) }
|
189
|
+
model.should_not have_scope(:reordered).reorder('id')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'with a select value' do
|
194
|
+
it 'matches if the scope exists and the query matches' do
|
195
|
+
model.instance_eval { scope(:ones, select('one')) }
|
196
|
+
model.should have_scope(:ones).select('one')
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'does not match if the query does not match' do
|
200
|
+
model.instance_eval { scope(:ones, select('one')) }
|
201
|
+
model.should_not have_scope(:ones).select('id')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'with a where value' do
|
206
|
+
it 'matches if the scope exists and the query matches' do
|
207
|
+
model.instance_eval { scope(:no_one, where(:one => nil)) }
|
208
|
+
model.should have_scope(:no_one).where(:one => nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'does not match if the query does not match' do
|
212
|
+
model.instance_eval { scope(:no_one, where(:one => nil)) }
|
213
|
+
model.should_not have_scope(:no_one).where(:one => 1)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with option :with' do
|
218
|
+
it 'matches if the query matches for the given value' do
|
219
|
+
model.instance_eval { scope(:which_one, lambda { |one| where(:one => one) }) }
|
220
|
+
model.should have_scope(:which_one, :with => 1).where(:one => 1)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'does not match if the query does not match for the given value' do
|
224
|
+
model.instance_eval { scope(:which_one, lambda { |one| where(:one => one) }) }
|
225
|
+
model.should_not have_scope(:which_one, :with => 2).where(:one => 1)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_associated do
|
4
|
+
let(:validator_name) { :associated }
|
5
|
+
let(:attribute) { :company }
|
6
|
+
let(:default_options) { { :on => :create } }
|
7
|
+
|
8
|
+
before do
|
9
|
+
create_table(:users) { |table| table.string(:company_id) }
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like 'a validation matcher' do
|
13
|
+
let(:model) do
|
14
|
+
build_class(:User, ActiveRecord::Base) do
|
15
|
+
include ActiveRecord::Validations
|
16
|
+
|
17
|
+
belongs_to :company
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
with_option(:message, 'is not unique!', 'invalid')
|
22
|
+
with_option(:on, :create, :update)
|
23
|
+
|
24
|
+
with_conditional_option(:if)
|
25
|
+
with_conditional_option(:unless)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_uniqueness_of do
|
4
|
+
let(:validator_name) { :uniqueness }
|
5
|
+
let(:default_options) { { :scope => :two } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
let(:model) do
|
9
|
+
build_class(:User) do
|
10
|
+
include ActiveRecord::Validations
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
with_option(:allow_blank, true, false)
|
15
|
+
with_option(:allow_nil, true, false)
|
16
|
+
with_option(:case_sensitive, true, false)
|
17
|
+
with_option(:message, 'is not unique!', 'invalid')
|
18
|
+
with_option(:scope, :two, :three)
|
19
|
+
|
20
|
+
with_conditional_option(:if)
|
21
|
+
with_conditional_option(:unless)
|
22
|
+
end
|
23
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
$:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
|
5
|
+
require 'remockable'
|
6
|
+
|
7
|
+
# Requires supporting files with custom matchers and macros, etc.,
|
8
|
+
# in ./support/ and its subdirectories.
|
9
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each do |file|
|
10
|
+
require(file)
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.mock_with :rspec
|
15
|
+
config.filter_run :focus => true
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecordExampleGroup
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
metadata[:type] = :active_record
|
6
|
+
|
7
|
+
let(:rake) { Rake::Application.new }
|
8
|
+
let(:task) { rake[self.class.description] }
|
9
|
+
let(:namespace) { self.class.description.split(':').first }
|
10
|
+
|
11
|
+
before do
|
12
|
+
ActiveRecord::Base.establish_connection(
|
13
|
+
:adapter => 'sqlite3',
|
14
|
+
:database => ':memory:'
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include self, :example_group => { :file_path => /spec\/active_record/ }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ClassBuilder
|
2
|
+
def build_class(name, superclass=Object, &block)
|
3
|
+
Class.new(superclass) do
|
4
|
+
class_eval <<-EVAL
|
5
|
+
def self.name
|
6
|
+
'#{name}'
|
7
|
+
end
|
8
|
+
EVAL
|
9
|
+
|
10
|
+
class_eval(&block) if block_given?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_table(table_name, options={}, &block)
|
15
|
+
begin
|
16
|
+
drop_table(table_name)
|
17
|
+
ActiveRecord::Base.connection.create_table(table_name, options, &block)
|
18
|
+
(@created_tables ||= []) << table_name
|
19
|
+
rescue
|
20
|
+
drop_table(table_name)
|
21
|
+
raise $!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def drop_table(table_name)
|
26
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def drop_created_tables
|
30
|
+
if @created_tables
|
31
|
+
@created_tables.each { |table_name| drop_table(table_name) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
RSpec.configure do |config|
|
37
|
+
config.include(ClassBuilder)
|
38
|
+
config.after { drop_created_tables }
|
39
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
shared_examples_for 'a validation matcher' do
|
2
|
+
let(:attribute) { :one }
|
3
|
+
let(:options) { default_options }
|
4
|
+
let(:matcher_name) { self.class.parent.parent.description }
|
5
|
+
|
6
|
+
let(:model) do
|
7
|
+
build_class(:User) do
|
8
|
+
include ActiveModel::Validations
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
model.validates(attribute, validator_name => options)
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { model.new }
|
17
|
+
|
18
|
+
def self.with_option(option_name, positive, negative, exclusive=false)
|
19
|
+
context "with option #{option_name.inspect}" do
|
20
|
+
let(:options) do
|
21
|
+
option = { option_name => positive }
|
22
|
+
merge_with_default = !exclusive && default_options.is_a?(Hash)
|
23
|
+
merge_with_default ? default_options.merge(option) : option
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'matches if the options match' do
|
27
|
+
should send(matcher_name, attribute, option_name => positive)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not match if the options do not match' do
|
31
|
+
should_not send(matcher_name, attribute, option_name => negative)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.with_option!(option_name, positive, negative)
|
37
|
+
with_option(option_name, positive, negative, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.with_conditional_option(option_name)
|
41
|
+
context "with option #{option_name.inspect} with a symbol" do
|
42
|
+
let(:options) {
|
43
|
+
option = { option_name => :returns_true }
|
44
|
+
default_options.is_a?(Hash) ? default_options.merge(option) : option
|
45
|
+
}
|
46
|
+
|
47
|
+
it 'matches if the options match' do
|
48
|
+
should send(matcher_name, attribute, option_name => :returns_true)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'does not match if the options do not match' do
|
52
|
+
should_not send(matcher_name, attribute, option_name => :returns_false)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with option #{option_name.inspect} with a procedure" do
|
57
|
+
let(:procedure) { lambda { |record| record.skip_validations } }
|
58
|
+
|
59
|
+
let(:options) {
|
60
|
+
option = { option_name => procedure }
|
61
|
+
default_options.is_a?(Hash) ? default_options.merge(option) : option
|
62
|
+
}
|
63
|
+
|
64
|
+
it 'matches if the options match' do
|
65
|
+
subject.stub(:skip_validations).and_return(true)
|
66
|
+
should send(matcher_name, attribute, option_name => true)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'does not match if the options do not match' do
|
70
|
+
subject.stub(:skip_validations).and_return(false)
|
71
|
+
should_not send(matcher_name, attribute, option_name => true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.with_unsupported_option(option_name, value=nil)
|
77
|
+
context "with unsupported option #{option_name.inspect}" do
|
78
|
+
it 'raises an error' do
|
79
|
+
expect {
|
80
|
+
send(matcher_name, option_name => value)
|
81
|
+
}.to raise_error(ArgumentError, /unsupported.*:#{option_name}/i)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'description' do
|
87
|
+
let(:matcher) { send(matcher_name, attribute) }
|
88
|
+
|
89
|
+
it 'has a custom description' do
|
90
|
+
name = matcher.instance_variable_get(:@name).to_s.gsub(/_/, ' ')
|
91
|
+
with = " with #{matcher.expected}" if matcher.expected.any?
|
92
|
+
|
93
|
+
matcher.description.should == "#{name} #{attribute}#{with}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'failure messages' do
|
98
|
+
let(:matcher) { send(matcher_name, attribute) }
|
99
|
+
|
100
|
+
before { matcher.matches?(subject) }
|
101
|
+
|
102
|
+
it 'has a custom failure message' do
|
103
|
+
matcher.failure_message_for_should.should ==
|
104
|
+
"Expected #{subject.class.name} to #{matcher.description}"
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'has a custom negative failure message' do
|
108
|
+
matcher.failure_message_for_should_not.should ==
|
109
|
+
"Did not expect #{subject.class.name} to #{matcher.description}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'without options' do
|
114
|
+
it 'matches if the validator has been defined' do
|
115
|
+
should send(matcher_name, :one)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not match if the validator has not been defined' do
|
119
|
+
should_not send(matcher_name, :two)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'with an unknown option' do
|
124
|
+
it 'raises an error' do
|
125
|
+
expect {
|
126
|
+
send(matcher_name, :xxx => true)
|
127
|
+
}.to raise_error(ArgumentError, /unknown.*:xxx/i)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
shared_examples_for 'an Active Record matcher' do
|
2
|
+
let(:matcher_name) { self.class.parent.parent.description }
|
3
|
+
|
4
|
+
def self.with_option(option_name, positive, negative)
|
5
|
+
context "with option #{option_name.inspect}" do
|
6
|
+
let(:options) { [:company, { option_name => positive }] }
|
7
|
+
|
8
|
+
before { model.send(macro, *options) }
|
9
|
+
|
10
|
+
it 'matches if the options match' do
|
11
|
+
should send(matcher_name, :company, option_name => positive)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'does not match if the options do not match' do
|
15
|
+
should_not send(matcher_name, :company, option_name => negative)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.with_unsupported_option(option_name, value=nil)
|
21
|
+
context "with unsupported option #{option_name.inspect}" do
|
22
|
+
it 'raises an error' do
|
23
|
+
expect {
|
24
|
+
send(matcher_name, :company, option_name => value)
|
25
|
+
}.to raise_error(ArgumentError, /unsupported.*:#{option_name}/i)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'failure messages' do
|
31
|
+
let(:matcher) { send(matcher_name, *options) }
|
32
|
+
|
33
|
+
before { matcher.matches?(subject) }
|
34
|
+
|
35
|
+
it 'has a custom failure message' do
|
36
|
+
matcher.failure_message_for_should.should ==
|
37
|
+
"Expected #{subject.class.name} to #{matcher.description}"
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'has a custom negative failure message' do
|
41
|
+
matcher.failure_message_for_should_not.should ==
|
42
|
+
"Did not expect #{subject.class.name} to #{matcher.description}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with an unknown option' do
|
47
|
+
it 'raises an error' do
|
48
|
+
expect {
|
49
|
+
send(matcher_name, :xxx => true)
|
50
|
+
}.to raise_error(ArgumentError, /unknown.*:xxx/i)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|