remockable 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Remockable
|
2
2
|
|
3
|
+
[![Build Status][travis-image]][travis]
|
4
|
+
|
5
|
+
[travis]: http://travis-ci.org/tylerhunt/remockable
|
6
|
+
[travis-image]: https://secure.travis-ci.org/tylerhunt/remockable.png
|
7
|
+
|
3
8
|
A collection of RSpec 2 matchers to simplify your web app specs.
|
4
9
|
|
5
10
|
|
@@ -7,7 +12,7 @@ A collection of RSpec 2 matchers to simplify your web app specs.
|
|
7
12
|
|
8
13
|
The goal of this project is to provide a modern replacement to the now
|
9
14
|
unmaintained Remarkable project. Remarkable was a great asset when Rails 2.3
|
10
|
-
was current, but now that Rails 3
|
15
|
+
was current, but now that Rails 3 has become mainstream, a gap has been left
|
11
16
|
by still unreleased Remarkable 4.0.
|
12
17
|
|
13
18
|
In looking at the code for Remarkable to determine the feasibility of continuing
|
@@ -17,58 +22,67 @@ Remockable was born. It's an attempt to start with a clean slate but maintain
|
|
17
22
|
the original goal of Remarkable in spirit.
|
18
23
|
|
19
24
|
|
20
|
-
##
|
25
|
+
## Installation
|
21
26
|
|
22
|
-
|
27
|
+
Add this line to your application's Gemfile:
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
* validate_exclusion_of
|
28
|
-
* validate_format_of
|
29
|
-
* validate_inclusion_of
|
30
|
-
* validate_length_of
|
31
|
-
* validate_numericality_of
|
32
|
-
* validate_presence_of
|
29
|
+
``` ruby
|
30
|
+
gem 'remockable'
|
31
|
+
```
|
33
32
|
|
33
|
+
And then execute:
|
34
34
|
|
35
|
-
|
35
|
+
$ bundle
|
36
36
|
|
37
|
-
|
37
|
+
Or install it yourself as:
|
38
38
|
|
39
|
-
|
40
|
-
* have_index
|
41
|
-
* have_scope
|
42
|
-
* have_default_scope
|
43
|
-
* belong_to
|
44
|
-
* have_one
|
45
|
-
* have_many
|
46
|
-
* have_and_belong_to_many
|
47
|
-
* validate_associated
|
48
|
-
* validate_uniqueness_of
|
39
|
+
$ gem install remockable
|
49
40
|
|
50
41
|
|
51
|
-
##
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
Remockable provides matchers for use with Active Model and Active Record
|
45
|
+
classes.
|
52
46
|
|
53
|
-
|
47
|
+
### Active Model Matchers
|
54
48
|
|
55
|
-
|
49
|
+
The following Active Model matchers are supported:
|
56
50
|
|
57
|
-
|
51
|
+
* `allow_mass_assignment_of`
|
52
|
+
* `validate_acceptance_of`
|
53
|
+
* `validate_confirmation_of`
|
54
|
+
* `validate_exclusion_of`
|
55
|
+
* `validate_format_of`
|
56
|
+
* `validate_inclusion_of`
|
57
|
+
* `validate_length_of`
|
58
|
+
* `validate_numericality_of`
|
59
|
+
* `validate_presence_of`
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
### Active Record Matchers
|
62
|
+
|
63
|
+
The following Active Record matchers are supported:
|
62
64
|
|
63
|
-
|
65
|
+
* `have_column`
|
66
|
+
* `have_index`
|
67
|
+
* `have_scope`
|
68
|
+
* `have_default_scope`
|
69
|
+
* `belong_to`
|
70
|
+
* `have_one`
|
71
|
+
* `have_many`
|
72
|
+
* `have_and_belong_to_many`
|
73
|
+
* `validate_associated`
|
74
|
+
* `validate_uniqueness_of`
|
64
75
|
|
65
76
|
|
66
|
-
##
|
77
|
+
## Contributing
|
67
78
|
|
68
|
-
|
69
|
-
|
79
|
+
1. Fork it.
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
81
|
+
3. Commit your changes (`git commit -am 'Added some feature'`).
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
83
|
+
5. Create a new Pull Request.
|
70
84
|
|
71
85
|
|
72
86
|
## Copyright
|
73
87
|
|
74
|
-
Copyright © 2010-
|
88
|
+
Copyright © 2010-2012 Tyler Hunt. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/lib/remockable/version.rb
CHANGED
data/remockable.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require './lib/remockable/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'remockable'
|
5
|
+
spec.version = Remockable::VERSION
|
6
|
+
spec.authors = ['Tyler Hunt']
|
7
|
+
spec.summary = 'A collection of RSpec matchers for web apps.'
|
8
|
+
spec.homepage = 'http://github.com/tylerhunt/remockable'
|
9
|
+
spec.license = 'MIT'
|
10
|
+
|
11
|
+
spec.add_dependency 'activemodel', '~> 3.0'
|
12
|
+
spec.add_dependency 'activerecord', '~> 3.0'
|
13
|
+
spec.add_dependency 'activesupport', '~> 3.0'
|
14
|
+
spec.add_dependency 'rspec-core', '~> 2.0'
|
15
|
+
spec.add_dependency 'rspec-expectations', '~> 2.0'
|
16
|
+
spec.add_dependency 'rspec-mocks', '~> 2.0'
|
17
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3.4'
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |file| File.basename(file) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :allow_mass_assignment_of do
|
4
|
+
let(:attribute) { :one }
|
5
|
+
let(:options) { {} }
|
6
|
+
let(:matcher_name) { self.class.parent.description }
|
7
|
+
|
8
|
+
let(:model) do
|
9
|
+
build_class(:User) { include ActiveModel::MassAssignmentSecurity }
|
10
|
+
end
|
11
|
+
|
12
|
+
subject { model.new }
|
13
|
+
|
14
|
+
context 'description' do
|
15
|
+
it 'has a custom description' do
|
16
|
+
matcher = allow_mass_assignment_of(attribute)
|
17
|
+
matcher.description.should == "allow mass-assignment of #{attribute}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'failure messages' do
|
22
|
+
let(:matcher) { allow_mass_assignment_of(attribute) }
|
23
|
+
|
24
|
+
before { matcher.matches?(subject) }
|
25
|
+
|
26
|
+
it 'has a custom failure message' do
|
27
|
+
matcher.failure_message_for_should.should ==
|
28
|
+
"Expected #{subject.class.name} to #{matcher.description}"
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has a custom negative failure message' do
|
32
|
+
matcher.failure_message_for_should_not.should ==
|
33
|
+
"Did not expect #{subject.class.name} to #{matcher.description}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'for accessible' do
|
38
|
+
before { model.attr_accessible(attribute, options) }
|
39
|
+
|
40
|
+
it 'matches if the attribute is accessible' do
|
41
|
+
should allow_mass_assignment_of(:one)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not match if the attribute is protected' do
|
45
|
+
should_not allow_mass_assignment_of(:two)
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with a role' do
|
49
|
+
let(:options) { { :as => :admin } }
|
50
|
+
|
51
|
+
it 'matches if the attribute is accessible' do
|
52
|
+
should allow_mass_assignment_of(:one, :as => :admin)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does not match if the attribute is protected' do
|
56
|
+
should_not allow_mass_assignment_of(:two, :as => :admin)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'for protected' do
|
62
|
+
let(:attribute) { :two }
|
63
|
+
|
64
|
+
before { model.attr_protected(attribute, options) }
|
65
|
+
|
66
|
+
it 'matches if the attribute is accessible' do
|
67
|
+
should allow_mass_assignment_of(:one)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'does not match if the attribute is protected' do
|
71
|
+
should_not allow_mass_assignment_of(:two)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with a role' do
|
75
|
+
let(:options) { { :as => :admin } }
|
76
|
+
|
77
|
+
it 'matches if the attribute is accessible' do
|
78
|
+
should allow_mass_assignment_of(:one, :as => :admin)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'does not match if the attribute is protected' do
|
82
|
+
should_not allow_mass_assignment_of(:two, :as => :admin)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :allow_values_for do
|
4
|
+
let(:attribute) { :one }
|
5
|
+
let(:values) { ['123'] }
|
6
|
+
let(:matcher_name) { self.class.parent.description }
|
7
|
+
|
8
|
+
let(:model) do
|
9
|
+
build_class(:User) { include ActiveModel::Validations }
|
10
|
+
end
|
11
|
+
|
12
|
+
before { model.validates(attribute, :format => /^\d+$/) }
|
13
|
+
|
14
|
+
subject { model.new }
|
15
|
+
|
16
|
+
context 'description' do
|
17
|
+
it 'has a custom description' do
|
18
|
+
matcher = allow_values_for(attribute, *values)
|
19
|
+
matcher.description.should == "allow the values #{values.collect(&:inspect).to_sentence} for #{attribute}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'failure messages' do
|
24
|
+
let(:matcher) { allow_values_for(attribute, *values) }
|
25
|
+
|
26
|
+
before { matcher.matches?(subject) }
|
27
|
+
|
28
|
+
it 'has a custom failure message' do
|
29
|
+
matcher.failure_message_for_should.should ==
|
30
|
+
"Expected #{subject.class.name} to #{matcher.description}"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has a custom negative failure message' do
|
34
|
+
matcher.failure_message_for_should_not.should ==
|
35
|
+
"Did not expect #{subject.class.name} to #{matcher.description}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'matches if the values are valid' do
|
40
|
+
should allow_values_for(:one, '123', '4567890')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not match if the values are invalid' do
|
44
|
+
should_not allow_values_for(:one, 'abc', 'abc123')
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_acceptance_of do
|
4
|
+
let(:validator_name) { :acceptance }
|
5
|
+
let(:default_options) { true }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:accept, 'TRUE', 'FALSE')
|
9
|
+
with_option(:allow_nil, true, false)
|
10
|
+
with_option(:message, 'must agree!', 'invalid')
|
11
|
+
with_option(:on, :create, :update)
|
12
|
+
|
13
|
+
with_conditional_option(:if)
|
14
|
+
with_conditional_option(:unless)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_confirmation_of do
|
4
|
+
let(:validator_name) { :confirmation }
|
5
|
+
let(:default_options) { true }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:message, 'must match!', 'invalid')
|
9
|
+
with_option(:on, :create, :update)
|
10
|
+
|
11
|
+
with_conditional_option(:if)
|
12
|
+
with_conditional_option(:unless)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_exclusion_of do
|
4
|
+
let(:validator_name) { :exclusion }
|
5
|
+
let(:default_options) { { :in => [true, false] } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:allow_blank, true, false)
|
9
|
+
with_option(:allow_nil, true, false)
|
10
|
+
with_option(:in, [true, false], %w(male female))
|
11
|
+
with_option(:message, 'is in list!', 'invalid')
|
12
|
+
|
13
|
+
with_conditional_option(:if)
|
14
|
+
with_conditional_option(:unless)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_format_of do
|
4
|
+
let(:validator_name) { :format }
|
5
|
+
let(:default_options) { { :with => /\d+/ } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:allow_blank, true, false)
|
9
|
+
with_option(:allow_nil, true, false)
|
10
|
+
with_option(:message, 'is the wrong format!', 'invalid')
|
11
|
+
with_option(:on, :create, :update)
|
12
|
+
with_option!(:with, /\d+/, /\w+/)
|
13
|
+
with_option!(:without, /\d+/, /\w+/)
|
14
|
+
|
15
|
+
with_conditional_option(:if)
|
16
|
+
with_conditional_option(:unless)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_inclusion_of do
|
4
|
+
let(:validator_name) { :inclusion }
|
5
|
+
let(:default_options) { { :in => [true, false] } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:allow_blank, true, false)
|
9
|
+
with_option(:allow_nil, true, false)
|
10
|
+
with_option(:in, [true, false], %w(male female))
|
11
|
+
with_option(:message, 'is not in list!', 'invalid')
|
12
|
+
|
13
|
+
with_conditional_option(:if)
|
14
|
+
with_conditional_option(:unless)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_length_of do
|
4
|
+
let(:validator_name) { :length }
|
5
|
+
let(:default_options) { { :is => 5 } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:allow_blank, true, false)
|
9
|
+
with_option(:allow_nil, true, false)
|
10
|
+
with_option(:in, 1..10, 1..5)
|
11
|
+
with_option(:is, 5, 10)
|
12
|
+
with_option(:maximum, 5, 10)
|
13
|
+
with_option(:message, 'is the wrong length!', 'invalid')
|
14
|
+
with_option(:minimum, 5, 10)
|
15
|
+
with_option(:on, :create, :update)
|
16
|
+
with_option(:too_long, 'is too long!', 'invalid')
|
17
|
+
with_option(:too_short, 'is too short!', 'invalid')
|
18
|
+
with_option(:within, 1..10, 1..5)
|
19
|
+
with_option(:wrong_length, 'is not five!', 'invalid')
|
20
|
+
|
21
|
+
with_conditional_option(:if)
|
22
|
+
with_conditional_option(:unless)
|
23
|
+
|
24
|
+
with_unsupported_option(:tokenizer, lambda { |string| string.scan(/\w+/) })
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_numericality_of do
|
4
|
+
let(:validator_name) { :numericality }
|
5
|
+
let(:default_options) { { :only_integer => true } }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher' do
|
8
|
+
with_option(:allow_nil, true, false)
|
9
|
+
with_option(:equal_to, 5, 10)
|
10
|
+
with_option(:even, true, false)
|
11
|
+
with_option(:greater_than, 5, 10)
|
12
|
+
with_option(:greater_than_or_equal_to, 5, 10)
|
13
|
+
with_option(:less_than, 5, 10)
|
14
|
+
with_option(:less_than_or_equal_to, 5, 10)
|
15
|
+
with_option(:message, 'is the wrong numericality!', 'invalid')
|
16
|
+
with_option(:odd, true, false)
|
17
|
+
with_option(:on, :create, :update)
|
18
|
+
with_option(:only_integer, true, false)
|
19
|
+
|
20
|
+
with_conditional_option(:if)
|
21
|
+
with_conditional_option(:unless)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :validate_presence_of do
|
4
|
+
let(:validator_name) { :presence }
|
5
|
+
let(:default_options) { true }
|
6
|
+
|
7
|
+
it_behaves_like 'a validation matcher', :presence do
|
8
|
+
with_option(:message, 'is required!', 'invalid')
|
9
|
+
with_option(:on, :create, :update)
|
10
|
+
|
11
|
+
with_conditional_option(:if)
|
12
|
+
with_conditional_option(:unless)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :accept_nested_attributes_for do
|
4
|
+
let(:macro) { :accepts_nested_attributes_for }
|
5
|
+
let(:options) { [:company, { :allow_destroy => true }] }
|
6
|
+
|
7
|
+
it_behaves_like 'an Active Record matcher' do
|
8
|
+
let(:matcher_name) { :accept_nested_attributes_for }
|
9
|
+
|
10
|
+
let(:model) do
|
11
|
+
build_class(:User, ActiveRecord::Base) { belongs_to :company }
|
12
|
+
end
|
13
|
+
|
14
|
+
before { create_table(:users) }
|
15
|
+
|
16
|
+
subject { model.new }
|
17
|
+
|
18
|
+
context 'description' do
|
19
|
+
let(:matcher) { send(matcher_name, *options) }
|
20
|
+
|
21
|
+
it 'has a custom description' do
|
22
|
+
association = matcher.instance_variable_get(:@association)
|
23
|
+
with = " with #{matcher.expected.inspect}" if matcher.expected.any?
|
24
|
+
matcher.description.should == "accept nested attributes for #{association}#{with}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with no options' do
|
29
|
+
let(:options) { :company }
|
30
|
+
|
31
|
+
it 'matches if the model accepts the nested attributes' do
|
32
|
+
model.accepts_nested_attributes_for(*options)
|
33
|
+
model.should accept_nested_attributes_for(*options)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not match if the model does not accept the nested attributes' do
|
37
|
+
model.should_not accept_nested_attributes_for(*options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
with_option(:allow_destroy, true, false)
|
42
|
+
with_option(:limit, 1, 2)
|
43
|
+
with_option(:update_only, true, false)
|
44
|
+
|
45
|
+
with_unsupported_option(:reject_if, :all_blank)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :belong_to do
|
4
|
+
let(:macro) { :belongs_to }
|
5
|
+
let(:options) { [:company, { :dependent => :destroy }] }
|
6
|
+
|
7
|
+
it_behaves_like 'an Active Record matcher' do
|
8
|
+
let(:model) { build_class(:User, ActiveRecord::Base) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
create_table(:users) { |table| table.integer(:company_id) }
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { model.new }
|
15
|
+
|
16
|
+
context 'description' do
|
17
|
+
let(:matcher) { send(matcher_name, *options) }
|
18
|
+
|
19
|
+
it 'has a custom description' do
|
20
|
+
name = matcher.instance_variable_get(:@name).to_s.gsub(/_/, ' ')
|
21
|
+
association = matcher.instance_variable_get(:@association)
|
22
|
+
with = " with #{matcher.expected.inspect}" if matcher.expected.any?
|
23
|
+
matcher.description.should == "#{name} #{association}#{with}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with no options' do
|
28
|
+
let(:options) { :company }
|
29
|
+
|
30
|
+
it 'matches if the association exists' do
|
31
|
+
model.belongs_to(*options)
|
32
|
+
model.should belong_to(*options)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does not match if the association does not exist' do
|
36
|
+
model.should_not belong_to(*options)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not match if the association is of the wrong type' do
|
40
|
+
model.has_many(*options)
|
41
|
+
model.should_not belong_to(*options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
with_option(:class_name, 'Company', 'Organization')
|
46
|
+
with_option(:conditions, { :id => 1 }, { :id => 2 })
|
47
|
+
with_option(:select, %w(id), %w(name))
|
48
|
+
with_option(:foreign_key, :company_id, :organization_id)
|
49
|
+
with_option(:primary_key, :id, :company_id)
|
50
|
+
with_option(:dependent, :destroy, :nullify)
|
51
|
+
with_option(:counter_cache, true, false)
|
52
|
+
with_option(:include, :users, :employees)
|
53
|
+
with_option(:polymorphic, true, false)
|
54
|
+
with_option(:readonly, true, false)
|
55
|
+
with_option(:validate, true, false)
|
56
|
+
with_option(:autosave, true, false)
|
57
|
+
with_option(:touch, true, false)
|
58
|
+
with_option(:inverse_of, :users, :employees)
|
59
|
+
|
60
|
+
with_unsupported_option(:extend, Module.new)
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :have_and_belong_to_many do
|
4
|
+
let(:macro) { :has_and_belongs_to_many }
|
5
|
+
let(:options) { [:tags, { :order => :name }] }
|
6
|
+
|
7
|
+
it_behaves_like 'an Active Record matcher' do
|
8
|
+
let(:model) { build_class(:User, ActiveRecord::Base) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
create_table(:users) { |table| table.string(:name) }
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { model.new }
|
15
|
+
|
16
|
+
context 'description' do
|
17
|
+
let(:matcher) { send(matcher_name, *options) }
|
18
|
+
|
19
|
+
it 'has a custom description' do
|
20
|
+
association = matcher.instance_variable_get(:@association)
|
21
|
+
with = " with #{matcher.expected.inspect}" if matcher.expected.any?
|
22
|
+
matcher.description.should == "have and belong to #{association}#{with}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with no options' do
|
27
|
+
let(:options) { :tags }
|
28
|
+
|
29
|
+
it 'matches if the association exists' do
|
30
|
+
model.has_and_belongs_to_many(*options)
|
31
|
+
model.should have_and_belong_to_many(*options)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not match if the association does not exist' do
|
35
|
+
model.should_not have_and_belong_to_many(*options)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not match if the association is of the wrong type' do
|
39
|
+
model.has_many(*options)
|
40
|
+
model.should_not have_and_belong_to_many(*options)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
with_option(:class_name, 'Tag', 'Role')
|
45
|
+
with_option(:join_table, 'tags_users', 'user_tags')
|
46
|
+
with_option(:foreign_key, :user_id, :tagged_id)
|
47
|
+
with_option(:association_foreign_key, :tag_id, :role_id)
|
48
|
+
with_option(:conditions, { :active => true }, { :active => false })
|
49
|
+
with_option(:order, :name, :created_at)
|
50
|
+
with_option(:uniq, true, false)
|
51
|
+
with_option(:finder_sql, 'SELECT * FROM user_tags', 'SELECT * FROM tags_users')
|
52
|
+
with_option(:counter_sql, 'SELECT COUNT(*) FROM user_tags', 'SELECT COUNT(*) FROM tags_users')
|
53
|
+
with_option(:delete_sql, 'DELETE FROM user_tags', 'DELETE FROM tags_users')
|
54
|
+
with_option(:insert_sql, 'INSERT INTO user_tags', 'INSERT INTO tags_users')
|
55
|
+
with_option(:include, :statistics, :changes)
|
56
|
+
with_option(:group, 'usage_count', 'changes_count')
|
57
|
+
with_option(:having, 'usage_count > 5', 'changes_count > 5')
|
58
|
+
with_option(:limit, 5, 10)
|
59
|
+
with_option(:offset, 10, 20)
|
60
|
+
with_option(:select, %w(id), %w(name))
|
61
|
+
with_option(:readonly, true, false)
|
62
|
+
with_option(:validate, true, false)
|
63
|
+
with_option(:autosave, true, false)
|
64
|
+
|
65
|
+
with_unsupported_option(:extend, Module.new)
|
66
|
+
end
|
67
|
+
end
|