shoulda-activemodel 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. data/lib/shoulda/active_model.rb +16 -0
  2. data/lib/shoulda/active_model/assertions.rb +61 -0
  3. data/lib/shoulda/active_model/helpers.rb +31 -0
  4. data/lib/shoulda/active_model/macros.rb +268 -0
  5. data/lib/shoulda/active_model/matchers.rb +30 -0
  6. data/lib/shoulda/active_model/matchers/allow_value_matcher.rb +102 -0
  7. data/lib/shoulda/active_model/matchers/ensure_inclusion_of_matcher.rb +87 -0
  8. data/lib/shoulda/active_model/matchers/ensure_length_of_matcher.rb +141 -0
  9. data/lib/shoulda/active_model/matchers/validate_acceptance_of_matcher.rb +41 -0
  10. data/lib/shoulda/active_model/matchers/validate_format_of_matcher.rb +67 -0
  11. data/lib/shoulda/active_model/matchers/validate_numericality_of_matcher.rb +39 -0
  12. data/lib/shoulda/active_model/matchers/validate_presence_of_matcher.rb +60 -0
  13. data/lib/shoulda/active_model/matchers/validate_uniqueness_of_matcher.rb +148 -0
  14. data/lib/shoulda/active_model/matchers/validation_matcher.rb +57 -0
  15. data/test/fail_macros.rb +39 -0
  16. data/test/matchers/active_model/ensure_inclusion_of_matcher_test.rb +80 -0
  17. data/test/matchers/active_model/ensure_length_of_matcher_test.rb +158 -0
  18. data/test/matchers/active_model/validate_acceptance_of_matcher_test.rb +44 -0
  19. data/test/matchers/active_model/validate_format_of_matcher_test.rb +39 -0
  20. data/test/matchers/active_model/validate_numericality_of_matcher_test.rb +52 -0
  21. data/test/matchers/active_model/validate_presence_of_matcher_test.rb +86 -0
  22. data/test/matchers/active_model/validate_uniqueness_of_matcher_test.rb +147 -0
  23. data/test/model_builder.rb +106 -0
  24. data/test/rspec_test.rb +207 -0
  25. data/test/test_helper.rb +20 -0
  26. metadata +106 -0
@@ -0,0 +1,86 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class ValidatePresenceOfMatcherTest < ActiveSupport::TestCase # :nodoc:
4
+
5
+ context "a required attribute" do
6
+ setup do
7
+ define_model :example, :attr => :string do
8
+ validates_presence_of :attr
9
+ end
10
+ @model = Example.new
11
+ end
12
+
13
+ should "require a value" do
14
+ assert_accepts validate_presence_of(:attr), @model
15
+ end
16
+
17
+ should "not override the default message with a blank" do
18
+ assert_accepts validate_presence_of(:attr).with_message(nil), @model
19
+ end
20
+ end
21
+
22
+ context "an optional attribute" do
23
+ setup do
24
+ @model = define_model(:example, :attr => :string).new
25
+ end
26
+
27
+ should "not require a value" do
28
+ assert_rejects validate_presence_of(:attr), @model
29
+ end
30
+ end
31
+
32
+ context "a required has_many association" do
33
+ setup do
34
+ define_model :child
35
+ @model = define_model :parent do
36
+ has_many :children
37
+ validates_presence_of :children
38
+ end.new
39
+ end
40
+
41
+ should "require the attribute to be set" do
42
+ assert_accepts validate_presence_of(:children), @model
43
+ end
44
+ end
45
+
46
+ context "an optional has_many association" do
47
+ setup do
48
+ define_model :child
49
+ @model = define_model :parent do
50
+ has_many :children
51
+ end.new
52
+ end
53
+
54
+ should "not require the attribute to be set" do
55
+ assert_rejects validate_presence_of(:children), @model
56
+ end
57
+ end
58
+
59
+ context "a required has_and_belongs_to_many association" do
60
+ setup do
61
+ define_model :child
62
+ @model = define_model :parent do
63
+ has_and_belongs_to_many :children
64
+ validates_presence_of :children
65
+ end.new
66
+ end
67
+
68
+ should "require the attribute to be set" do
69
+ assert_accepts validate_presence_of(:children), @model
70
+ end
71
+ end
72
+
73
+ context "an optional has_and_belongs_to_many association" do
74
+ setup do
75
+ define_model :child
76
+ @model = define_model :parent do
77
+ has_and_belongs_to_many :children
78
+ end.new
79
+ end
80
+
81
+ should "not require the attribute to be set" do
82
+ assert_rejects validate_presence_of(:children), @model
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,147 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class ValidateUniquenessOfMatcherTest < ActiveSupport::TestCase # :nodoc:
4
+
5
+ context "a unique attribute" do
6
+ setup do
7
+ @model = define_model(:example, :attr => :string,
8
+ :other => :integer) do
9
+ validates_uniqueness_of :attr
10
+ end.new
11
+ end
12
+
13
+ context "with an existing value" do
14
+ setup do
15
+ @existing = Example.create!(:attr => 'value', :other => 1)
16
+ end
17
+
18
+ should "require a unique value for that attribute" do
19
+ assert_accepts validate_uniqueness_of(:attr), @model
20
+ end
21
+
22
+ should "pass when the subject is an existing record" do
23
+ assert_accepts validate_uniqueness_of(:attr), @existing
24
+ end
25
+
26
+ should "fail when a scope is specified" do
27
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:other), @model
28
+ end
29
+ end
30
+
31
+ context "without an existing value" do
32
+ setup do
33
+ assert_nil Example.find(:first)
34
+ @matcher = validate_uniqueness_of(:attr)
35
+ end
36
+
37
+ should "fail to require a unique value" do
38
+ assert_rejects @matcher, @model
39
+ end
40
+
41
+ should "alert the tester that an existing value is not present" do
42
+ @matcher.matches?(@model)
43
+ assert @matcher.negative_failure_message =~ /^Can't find first .*/
44
+ end
45
+ end
46
+ end
47
+
48
+ context "a unique attribute with a custom error and an existing value" do
49
+ setup do
50
+ @model = define_model(:example, :attr => :string) do
51
+ validates_uniqueness_of :attr, :message => 'Bad value'
52
+ end.new
53
+ Example.create!
54
+ end
55
+
56
+ should "fail when checking the default message" do
57
+ assert_rejects validate_uniqueness_of(:attr), @model
58
+ end
59
+
60
+ should "fail when checking a message that doesn't match" do
61
+ assert_rejects validate_uniqueness_of(:attr).with_message(/abc/i), @model
62
+ end
63
+
64
+ should "pass when checking a message that matches" do
65
+ assert_accepts validate_uniqueness_of(:attr).with_message(/bad/i), @model
66
+ end
67
+ end
68
+
69
+ context "a scoped unique attribute with an existing value" do
70
+ setup do
71
+ @model = define_model(:example, :attr => :string,
72
+ :scope1 => :integer,
73
+ :scope2 => :integer) do
74
+ validates_uniqueness_of :attr, :scope => [:scope1, :scope2]
75
+ end.new
76
+ @existing = Example.create!(:attr => 'value', :scope1 => 1, :scope2 => 2)
77
+ end
78
+
79
+ should "pass when the correct scope is specified" do
80
+ assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
81
+ @model
82
+ end
83
+
84
+ should "pass when the subject is an existing record" do
85
+ assert_accepts validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2),
86
+ @existing
87
+ end
88
+
89
+ should "fail when a different scope is specified" do
90
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:scope1), @model
91
+ end
92
+
93
+ should "fail when no scope is specified" do
94
+ assert_rejects validate_uniqueness_of(:attr), @model
95
+ end
96
+
97
+ should "fail when a non-existent attribute is specified as a scope" do
98
+ assert_rejects validate_uniqueness_of(:attr).scoped_to(:fake), @model
99
+ end
100
+ end
101
+
102
+ context "a non-unique attribute with an existing value" do
103
+ setup do
104
+ @model = define_model(:example, :attr => :string).new
105
+ Example.create!(:attr => 'value')
106
+ end
107
+
108
+ should "not require a unique value for that attribute" do
109
+ assert_rejects validate_uniqueness_of(:attr), @model
110
+ end
111
+ end
112
+
113
+ context "a case sensitive unique attribute with an existing value" do
114
+ setup do
115
+ @model = define_model(:example, :attr => :string) do
116
+ validates_uniqueness_of :attr, :case_sensitive => true
117
+ end.new
118
+ Example.create!(:attr => 'value')
119
+ end
120
+
121
+ should "not require a unique, case-insensitive value for that attribute" do
122
+ assert_rejects validate_uniqueness_of(:attr).case_insensitive, @model
123
+ end
124
+
125
+ should "require a unique, case-sensitive value for that attribute" do
126
+ assert_accepts validate_uniqueness_of(:attr), @model
127
+ end
128
+ end
129
+
130
+ context "a case sensitive unique integer attribute with an existing value" do
131
+ setup do
132
+ @model = define_model(:example, :attr => :integer) do
133
+ validates_uniqueness_of :attr, :case_sensitive => true
134
+ end.new
135
+ Example.create!(:attr => 'value')
136
+ end
137
+
138
+ should "require a unique, case-insensitive value for that attribute" do
139
+ assert_accepts validate_uniqueness_of(:attr).case_insensitive, @model
140
+ end
141
+
142
+ should "require a unique, case-sensitive value for that attribute" do
143
+ assert_accepts validate_uniqueness_of(:attr), @model
144
+ end
145
+ end
146
+
147
+ end
@@ -0,0 +1,106 @@
1
+ class ActiveSupport::TestCase
2
+ def create_table(table_name, &block)
3
+ connection = ActiveRecord::Base.connection
4
+
5
+ begin
6
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
7
+ connection.create_table(table_name, &block)
8
+ @created_tables ||= []
9
+ @created_tables << table_name
10
+ connection
11
+ rescue Exception => e
12
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
13
+ raise e
14
+ end
15
+ end
16
+
17
+ def define_constant(class_name, base, &block)
18
+ class_name = class_name.to_s.camelize
19
+
20
+ klass = Class.new(base)
21
+ Object.const_set(class_name, klass)
22
+
23
+ klass.class_eval(&block) if block_given?
24
+
25
+ @defined_constants ||= []
26
+ @defined_constants << class_name
27
+
28
+ klass
29
+ end
30
+
31
+ def define_model_class(class_name, &block)
32
+ define_constant(class_name, ActiveRecord::Base, &block)
33
+ end
34
+
35
+ def define_model(name, columns = {}, &block)
36
+ class_name = name.to_s.pluralize.classify
37
+ table_name = class_name.tableize
38
+
39
+ create_table(table_name) do |table|
40
+ columns.each do |name, type|
41
+ table.column name, type
42
+ end
43
+ end
44
+
45
+ define_model_class(class_name, &block)
46
+ end
47
+
48
+ def define_controller(class_name, &block)
49
+ class_name = class_name.to_s
50
+ class_name << 'Controller' unless class_name =~ /Controller$/
51
+ define_constant(class_name, ActionController::Base, &block)
52
+ end
53
+
54
+ def define_routes(&block)
55
+ @replaced_routes = ActionController::Routing::Routes
56
+ new_routes = ActionController::Routing::RouteSet.new
57
+ silence_warnings do
58
+ ActionController::Routing.const_set('Routes', new_routes)
59
+ end
60
+ new_routes.draw(&block)
61
+ end
62
+
63
+ def build_response(&block)
64
+ klass = define_controller('Examples')
65
+ block ||= lambda { render :nothing => true }
66
+ klass.class_eval { define_method(:example, &block) }
67
+ define_routes do |map|
68
+ map.connect 'examples', :controller => 'examples', :action => 'example'
69
+ end
70
+
71
+ @controller = klass.new
72
+ @request = ActionController::TestRequest.new
73
+ @response = ActionController::TestResponse.new
74
+ get :example
75
+
76
+ @controller
77
+ end
78
+
79
+ def teardown_with_models
80
+ if @defined_constants
81
+ @defined_constants.each do |class_name|
82
+ Object.send(:remove_const, class_name)
83
+ end
84
+ end
85
+
86
+ if @created_tables
87
+ @created_tables.each do |table_name|
88
+ ActiveRecord::Base.
89
+ connection.
90
+ execute("DROP TABLE IF EXISTS #{table_name}")
91
+ end
92
+ end
93
+
94
+ if @replaced_routes
95
+ ActionController::Routing::Routes.clear!
96
+ silence_warnings do
97
+ ActionController::Routing.const_set('Routes', @replaced_routes)
98
+ end
99
+ @replaced_routes.reload!
100
+ end
101
+
102
+ teardown_without_models
103
+ end
104
+ alias_method :teardown_without_models, :teardown
105
+ alias_method :teardown, :teardown_with_models
106
+ end
@@ -0,0 +1,207 @@
1
+ require 'test_helper'
2
+
3
+ begin
4
+ gem 'rspec'
5
+ gem 'rspec-rails'
6
+ rescue LoadError => exception
7
+ puts exception.message
8
+ puts "RSpec integration was not tested because RSpec is not available"
9
+ else
10
+
11
+ class RspecTest < ActiveSupport::TestCase
12
+
13
+ SHOULDA_ROOT =
14
+ File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
15
+
16
+ def setup
17
+ build_gemspec
18
+ end
19
+
20
+ def teardown
21
+ FileUtils.rm_rf(project_dir)
22
+ FileUtils.rm_rf("#{shoulda_root}/pkg")
23
+ end
24
+
25
+ should "integrate correctly when using config.gem in test.rb" do
26
+ create_project
27
+ insert(rspec_dependencies, "config/environments/test.rb")
28
+ vendor_gems('test')
29
+ configure_spec_rails
30
+ assert_configured
31
+ end
32
+
33
+ should "integrate correctly when using config.gem in environment.rb" do
34
+ create_project
35
+ insert(rspec_dependencies,
36
+ "config/environment.rb",
37
+ /Rails::Initializer\.run/)
38
+ vendor_gems('development')
39
+ configure_spec_rails
40
+ assert_configured
41
+ end
42
+
43
+ should "integrate correctly when using require in spec_helper" do
44
+ create_project
45
+ configure_spec_rails
46
+ insert(%{gem 'shoulda'; require 'shoulda'},
47
+ "spec/spec_helper.rb",
48
+ %{require 'spec/rails'})
49
+ assert_configured
50
+ end
51
+
52
+ should "integrate correctly when unpacked and required in spec_helper" do
53
+ create_project
54
+ configure_spec_rails
55
+ insert(%{require 'shoulda'},
56
+ "spec/spec_helper.rb",
57
+ %{require 'spec/rails'})
58
+ unpack_gems
59
+ assert_configured
60
+ end
61
+
62
+ def create_project
63
+ command "rails #{project_dir}"
64
+ end
65
+
66
+ def vendor_gems(env)
67
+ project_command "rake gems:unpack RAILS_ENV=#{env}"
68
+ end
69
+
70
+ def unpack_gems
71
+ FileUtils.mkdir_p "#{project_dir}/vendor/gems"
72
+ FileUtils.cd "#{project_dir}/vendor/gems" do
73
+ %w(rspec rspec-rails shoulda).each do |gem|
74
+ command "gem unpack #{gem}"
75
+ end
76
+ end
77
+
78
+ insert('config.load_paths += Dir["#{RAILS_ROOT}/vendor/gems/*/lib"]',
79
+ "config/environment.rb",
80
+ /Rails::Initializer\.run/)
81
+ end
82
+
83
+ def command(command)
84
+ output = `GEM_PATH=#{shoulda_root}/pkg #{command} 2>&1`
85
+ unless $? == 0
86
+ flunk("Command failed with status #{$?}\n#{command}\n#{output}")
87
+ end
88
+ @command_output ||= ''
89
+ @command_output << output
90
+ output
91
+ end
92
+
93
+ def project_command(command)
94
+ result = nil
95
+ FileUtils.cd project_dir do
96
+ result = command(command)
97
+ end
98
+ result
99
+ end
100
+
101
+ def shoulda_command(command)
102
+ FileUtils.cd shoulda_root do
103
+ command(command)
104
+ end
105
+ end
106
+
107
+ def project_name
108
+ 'example_rails_project'
109
+ end
110
+
111
+ def project_dir
112
+ File.expand_path(File.join(File.dirname(__FILE__), project_name))
113
+ end
114
+
115
+ def insert(content, path, after = nil)
116
+ path = File.join(project_dir, path)
117
+ contents = IO.read(path)
118
+ if after
119
+ contents.gsub!(/^(.*#{after}.*)$/, "\\1\n#{content}")
120
+ else
121
+ contents << "\n" << content
122
+ end
123
+ File.open(path, 'w') {|file| file.write(contents) }
124
+ end
125
+
126
+ def rspec_dependencies
127
+ return <<-EOS
128
+ config.gem 'rspec', :lib => 'spec'
129
+ config.gem 'rspec-rails', :lib => false
130
+ config.gem 'shoulda', :lib => 'shoulda'
131
+ EOS
132
+ end
133
+
134
+ def configure_spec_rails
135
+ project_command "script/generate rspec"
136
+ end
137
+
138
+ def assert_configured
139
+ create_model
140
+ migrate
141
+ create_controller
142
+ assert_spec_passes
143
+ end
144
+
145
+ def create_model
146
+ project_command "script/generate rspec_model person name:string"
147
+ insert "validates_presence_of :name",
148
+ "app/models/person.rb",
149
+ /class Person/
150
+ insert "it { should validate_presence_of(:name) }",
151
+ "spec/models/person_spec.rb",
152
+ /describe Person do/
153
+ end
154
+
155
+ def create_controller
156
+ project_command "script/generate rspec_controller people"
157
+ insert "def index; render :text => 'Hello'; end",
158
+ "app/controllers/people_controller.rb",
159
+ /class PeopleController/
160
+ shoulda_controller_example = <<-EOS
161
+ describe PeopleController, "on GET index" do
162
+ integrate_views
163
+ subject { controller }
164
+ before(:each) { get :index }
165
+ it { should respond_with(:success) }
166
+ end
167
+ EOS
168
+ insert shoulda_controller_example,
169
+ "spec/controllers/people_controller_spec.rb"
170
+ end
171
+
172
+ def migrate
173
+ project_command "rake db:migrate"
174
+ end
175
+
176
+ def assert_spec_passes
177
+ result = project_command("rake spec SPEC_OPTS=-fs")
178
+ assert_match /should require name to be set/, result
179
+ assert_match /should respond with 200/, result
180
+ end
181
+
182
+ def shoulda_root
183
+ SHOULDA_ROOT
184
+ end
185
+
186
+ def build_gemspec
187
+ backup_gemspec do
188
+ shoulda_command "rake gemspec"
189
+ shoulda_command "rake gem"
190
+ shoulda_command "gem install --no-ri --no-rdoc -i pkg pkg/shoulda*.gem"
191
+ end
192
+ end
193
+
194
+ def backup_gemspec
195
+ actual = "#{shoulda_root}/shoulda.gemspec"
196
+ backup = "#{shoulda_root}/backup.gemspec"
197
+ FileUtils.mv(actual, backup)
198
+ begin
199
+ yield
200
+ ensure
201
+ FileUtils.mv(backup, actual)
202
+ end
203
+ end
204
+
205
+ end
206
+
207
+ end