database_tester 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0371542429b7f8340e494939dfc0704c829824c9
4
+ data.tar.gz: e4277696442164a124c214baa29ff42510a3ed84
5
+ SHA512:
6
+ metadata.gz: 6e282512a5e8a1622d8cc514beb8a99cce85623cfddc03792a46195f5dc6e1b5963e51824095f7bdc580dc3c87554ae9efd816f03df6622e6b032dbdf8b6a282
7
+ data.tar.gz: 333f478bc7f64b2cc20cee33d3090931760f9c1ce8a71fd1879feed9a3b8ecff6dc122f869a9a575b628dfac48e2fb125866c1d39248800b533fe99a90ed7b96
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in database_tester.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Aleksandar Zoric
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ [![Gem Version](https://badge.fury.io/rb/database_tester.svg)](http://badge.fury.io/rb/database_tester)
2
+
3
+ # Database::Tester
4
+
5
+ Testing database methods made easy.
6
+
7
+ The gem provides `RSpec` tests for most of the ActiveRecord methods. Tested on `MySQL`, `PostgreSQL`, `Derby` and `SpliceMachine` databases.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile, under `test` group:
12
+
13
+ ```ruby
14
+ group :test do
15
+ gem 'database_tester'
16
+ end
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install database_tester
26
+
27
+ In your `spec_helper.rb` add on top of your file:
28
+
29
+ require 'database/tester'
30
+
31
+ ## Usage
32
+
33
+ The gem is mostly a sum of RSpes shared examples. In order to use the gem's functions, write `RSpec` using:
34
+
35
+ ```ruby
36
+ let(:model) { described_class }
37
+ subject(:item) { create model } # (You can use "let" as well)
38
+ ```
39
+
40
+ - `:model` is used to test the database functions agains the model that is specified in this variable.
41
+
42
+ - `:item` is the actual database record against which the tests will be run. This object is mostly automatically created within most of the tests, but it is needed for testing CRUD operations, so make sure it it initialized when testing `create`, `update` etc. methods.
43
+
44
+
45
+ ## Examples and List of Methods:
46
+
47
+ ```ruby
48
+ describe Company do
49
+ let(:model) { described_class }
50
+
51
+ it { should have_db_column(:name).of_type(:string) }
52
+ it { should have_many(:users) }
53
+ it { should have_many(:profiles).through(:users) }
54
+ it { should have_one(:address) }
55
+
56
+
57
+ describe 'CRUD operations' do
58
+ subject(:item) { create model }
59
+
60
+ it_behaves_like 'model create'
61
+ it_behaves_like 'model update', :name, 'New Company Name'
62
+ it_behaves_like 'model readonly update', :description, 'Company Description'
63
+ it_behaves_like 'model destroy'
64
+ it_behaves_like 'model create_with', :name, 'New Company', :description, 'This Is A New Company'
65
+ end
66
+
67
+ describe 'Validation' do
68
+ it_behaves_like 'model validation', :name
69
+ end
70
+
71
+ describe 'Associations' do
72
+ it_behaves_like 'has_many association', User
73
+ it_behaves_like 'has_one association', Profile
74
+ it_behaves_like 'belongs_to association', Organization, true
75
+ it_behaves_like 'polymorphic association', :address, Address
76
+ it_behaves_like 'has_many through association', Profile, User
77
+ it_behaves_like 'habtm association', Tag
78
+ end
79
+
80
+ describe 'Selectors' do
81
+ it_behaves_like 'selector', :all_records
82
+ it_behaves_like 'selector', :ordered
83
+ it_behaves_like 'selector', :reverse_ordered
84
+ it_behaves_like 'selector', :limited
85
+ it_behaves_like 'selector', :selected
86
+ it_behaves_like 'selector', :grouped, group_by: :name, options: { name: 'New Name' }
87
+ it_behaves_like 'selector', :offsetted
88
+ it_behaves_like 'selector all with update', :name, { name: 'New Company' }
89
+ it_behaves_like 'find selector'
90
+ it_behaves_like 'join and include query', User
91
+ it_behaves_like 'distinct selector', :name
92
+ it_behaves_like 'eager_load selector', :users
93
+ it_behaves_like 'preload selector', :users
94
+ it_behaves_like 'references selector', :users
95
+ it_behaves_like 'reverse_order selector'
96
+ it_behaves_like 'find_by selector', :name, 'name1', 'name2'
97
+ it_behaves_like 'range conditions'
98
+ it_behaves_like 'subset conditions'
99
+ it_behaves_like 'find_or_create_by selector', :name, 'Company1'
100
+ it_behaves_like 'find_or_create_by! selector', :name, 'Company1'
101
+ it_behaves_like 'find_or_initialize_by selector', :name, 'Company1'
102
+ it_behaves_like 'select_all selector'
103
+ it_behaves_like 'pluck selector', :name
104
+ it_behaves_like 'ids selector'
105
+ it_behaves_like 'exists? selector', :name, 'val1', 'val2'
106
+ it_behaves_like 'minimum selector', :name, ['3', '5', '1', '4', '2']
107
+ it_behaves_like 'raw sql selector', :name, 'TestName'
108
+ end
109
+
110
+ describe 'Additional functions' do
111
+ it_behaves_like 'pessimistic locking'
112
+ it_behaves_like 'extending scope'
113
+ it_behaves_like 'none relation'
114
+ end
115
+ end
116
+ ```
117
+
118
+ ## Testing
119
+
120
+ To test the code run in your project directory:
121
+
122
+ ```ruby
123
+ bundle exec rspec spec/
124
+ ```
125
+
126
+ ## Contributing
127
+
128
+ Bug reports and pull requests are always welcome.
129
+
130
+ 1. Fork it ( https://github.com/aleksandaar/database_tester/fork )
131
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
132
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
133
+ 4. Push to the branch (`git push origin my-new-feature`)
134
+ 5. Create a new Pull Request
135
+
136
+
137
+ ## Ruby versions
138
+
139
+ Currently tested on **Ruby** `2.3.1` and **JRuby** `9.1.5.0`
140
+
141
+
142
+ ## Credits
143
+
144
+ `Database Tester` is developed and maintained by [Aleksandar Zoric](https://github.com/aleksandaar "Aleksandar Zoric")
145
+
146
+ ## License
147
+
148
+ MIT License. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "database/tester"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "database/tester/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "database_tester"
8
+ spec.version = Database::Tester::VERSION
9
+ spec.authors = ["Aleksandar Zoric"]
10
+ spec.email = ["aleksandroz.mail@gmail.com"]
11
+
12
+ spec.summary = %q{Testing database methods made easy.}
13
+ spec.description = %q{Test ActiveRecord methods against specific models in your database.}
14
+ spec.homepage = "http://aleksandroz.com"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.15"
33
+ spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ end
@@ -0,0 +1,5 @@
1
+ module Database
2
+ module Tester
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,559 @@
1
+ require "database/tester/version"
2
+
3
+ module Database
4
+ module Tester
5
+ RSpec.shared_examples 'model create' do
6
+ it 'is created' do
7
+ expect{ create(model) }.to change(model, :count).by(1)
8
+ end
9
+ end
10
+
11
+ RSpec.shared_examples 'model update' do |field, value|
12
+
13
+ it 'is updated' do
14
+ old_field = item.send(field)
15
+ expect{item.update field => value}.to change{item.send(field)}.from(old_field).to(value)
16
+ end
17
+ end
18
+
19
+ RSpec.shared_examples 'model readonly update' do |field, value|
20
+ before { item.update field => value }
21
+
22
+ it 'is not updated' do
23
+ expect(model.where(id: item.id)[0].send(field)).not_to eq value
24
+ end
25
+ end
26
+
27
+ RSpec.shared_examples 'model destroy' do
28
+ before { item.save }
29
+
30
+ it 'is destroyed' do
31
+ expect{ item.destroy }.to change(model, :count).by(-1)
32
+ end
33
+ end
34
+
35
+ RSpec.shared_examples 'model create_with' do |find_by_field, find_by_value, field, value|
36
+ it 'creates with value' do
37
+ expect{ model.create_with(field => value).find_or_create_by(find_by_field => find_by_value) }.to change(model, :count).by(1)
38
+ expect(model.order(id: :desc)[0][find_by_field]).to eq(find_by_value)
39
+ end
40
+
41
+ it 'returns an existing model' do
42
+ model.create!(find_by_field => find_by_value)
43
+ expect{ model.create_with(field => value).find_or_create_by(find_by_field => find_by_value) }.to change(model, :count).by(0)
44
+ end
45
+ end
46
+
47
+
48
+ RSpec.shared_examples 'model validation' do |*fields|
49
+ let(:new_item) { model.new }
50
+
51
+ it 'is not created if it is not valid' do
52
+ expect(new_item).to_not be_valid
53
+ fields.each do |field|
54
+ expect(new_item.errors[field].any?).to be_truthy
55
+ end
56
+ end
57
+ end
58
+
59
+ RSpec.shared_examples 'belongs_to association' do |model_associated, required = false|
60
+ let (:item) { create model }
61
+ let (:item_associated) { create model_associated }
62
+ let (:model_association) { model_associated.to_s.downcase }
63
+
64
+ it "adds #{model_associated}" do
65
+ item.send("#{model_association}=", item_associated)
66
+ item.save
67
+ expect(model.where(id: item.id)[0].send(model_association)).to eq item_associated
68
+ end
69
+
70
+ if required
71
+ it "does not remove #{model_associated}" do
72
+ item.send("#{model_association}=", item_associated)
73
+ item.save
74
+ item.send("#{model_association}=", nil)
75
+ expect(item).to_not be_valid
76
+ end
77
+ end
78
+ end
79
+
80
+ RSpec.shared_examples 'has_one association' do |model_associated, required = false|
81
+ let (:item) { create model }
82
+ let (:item_associated) { create model_associated }
83
+ let (:model_association) { model_associated.to_s.downcase }
84
+
85
+ before do
86
+ item.send("#{model_association}=", item_associated)
87
+ item.save
88
+ end
89
+
90
+ it "adds #{model_associated}" do
91
+ expect(model.where(id: item.id)[0].send(model_association)).to eq item_associated
92
+ end
93
+
94
+ if required
95
+ it "does not remove #{model_associated}" do
96
+ item.send("#{model_association}=", nil)
97
+ expect(item).to_not be_valid
98
+ end
99
+ end
100
+ end
101
+
102
+ RSpec.shared_examples 'has_many association' do |model_associated, required = false|
103
+ let (:item) { create model }
104
+ let (:item_associated) { create model_associated }
105
+ let (:model_association) { model_associated.model_name.plural }
106
+
107
+ before do
108
+ item.send(model_association) << item_associated
109
+ end
110
+
111
+ it "adds #{model_associated}" do
112
+ if required
113
+ expect(item.send(model_association).count).to eq 2
114
+ expect(item.send(model_association)[1]).to eq item_associated
115
+ else
116
+ expect(item.send(model_association).count).to eq 1
117
+ expect(item.send(model_association)[0]).to eq item_associated
118
+ end
119
+ end
120
+
121
+ it "removes #{model_associated}" do
122
+ item.send(model_association).delete(item_associated)
123
+ if required
124
+ expect(item.send(model_association).count).to eq 1
125
+ expect(item.send(model_association)[0]).to_not be_nil
126
+ else
127
+ expect(item.send(model_association).count).to eq 0
128
+ expect(item.send(model_association)[0]).to be_nil
129
+ end
130
+ end
131
+
132
+ it "destroys dependent #{model_associated}" do
133
+ item.destroy
134
+ expect(item.send(model_association).count).to eq 0
135
+ expect(item.send(model_association)[0]).to be_nil
136
+ end
137
+ end
138
+
139
+ RSpec.shared_examples 'habtm association' do |model_associated, required = false|
140
+ let (:item) { create model }
141
+ let (:item_associated) { create model_associated }
142
+ let (:model_association) { model_associated.model_name.plural }
143
+
144
+ it "adds #{model_associated}" do
145
+ item.send(model_association) << item_associated
146
+ if required
147
+ expect(item.send(model_association).count).to eq 2
148
+ else
149
+ expect(item.send(model_association).count).to eq 1
150
+ end
151
+ expect(item.send(model_association).order(id: :desc)[0]).to eq item_associated
152
+ end
153
+
154
+ it "creates #{model_associated}" do
155
+ item.send(model_association).send(:create, item_associated.attributes.except('id', 'created_at', 'updated_at'))
156
+ expect(item.send(model_association).count).to eq 1
157
+ end
158
+
159
+ it "removes #{model_associated}" do
160
+ item.send(model_association) << item_associated
161
+ item.send(model_association).delete(item_associated)
162
+ if required
163
+ expect(item.send(model_association).count).to eq 1
164
+ expect(item.send(model_association)[0]).to_not be_nil
165
+ else
166
+ expect(item.send(model_association).count).to eq 0
167
+ expect(item.send(model_association)[0]).to be_nil
168
+ end
169
+ end
170
+ end
171
+
172
+ RSpec.shared_examples 'has_many through association' do |model_associated, model_through|
173
+ let (:item) { create model }
174
+ let (:base_model_association) { model.to_s.downcase }
175
+ let (:model_association) { model_associated.to_s.downcase }
176
+ let (:model_association_pl) { model_associated.model_name.plural }
177
+ let (:model_through_association) { model_through.to_s.downcase }
178
+ let (:model_through_association_pl) { model_through.model_name.plural }
179
+
180
+ let! (:item_through) { create model_through, base_model_association => item }
181
+ let! (:item_associated) { create model_associated, model_through_association => item_through}
182
+
183
+ it "creates #{model_associated}" do
184
+ expect(item.send(model_association_pl).count).to eq 1
185
+ end
186
+
187
+ it "deletes #{model_associated}" do
188
+ item.send(model_through_association_pl).send(:destroy_all)
189
+ expect(item.send(model_association_pl).count).to eq 0
190
+ end
191
+
192
+ end
193
+
194
+
195
+ RSpec.shared_examples 'polymorphic association' do |model_association, *models_associated|
196
+ let (:item) { create model }
197
+
198
+ models_associated.each do |model_associated|
199
+ it "has an association with #{model_associated}" do
200
+ item_associated = create model_associated
201
+ item.send "#{model_association}=", item_associated
202
+ expect(item.send(model_association)).to be_an(model_associated)
203
+ end
204
+ end
205
+ end
206
+
207
+ RSpec.shared_examples 'join and include query' do |model_associated|
208
+ let! (:item) { create model }
209
+ let (:item_associated) { create model_associated }
210
+ let (:model_association) { model_associated.model_name.plural }
211
+
212
+ before do
213
+ item.send(model_association) << item_associated
214
+ end
215
+
216
+ it ".joins #{model_associated}" do
217
+ item_relation = model.send(:where, {id: item.id})
218
+ item_relation.joins(model_association.downcase.to_sym).each do |item_object|
219
+ expect(item_object.send(model_association).count).to eq 1
220
+ end
221
+ end
222
+
223
+ it ".includes #{model_associated}" do
224
+ model.send(:where, {id: item.id}).includes(model_association.downcase.to_sym).each do |item_object|
225
+ expect(item_object.send(model_association).count).to eq 1
226
+ end
227
+ end
228
+ end
229
+
230
+ RSpec.shared_examples 'pessimistic locking' do
231
+ let (:item) { create model }
232
+
233
+ it 'locks table row' do
234
+ ActiveRecord::Base.transaction do
235
+ locked_item = model.find(item.id).lock!
236
+ expect(locked_item.update_attributes({})).to eq true
237
+ end
238
+ end
239
+ end
240
+
241
+
242
+ RSpec.shared_examples 'selector' do |scope_action, group_by: nil, options: {}|
243
+ describe ".#{scope_action}" do
244
+ let!(:item_1) { create model, options }
245
+ let!(:item_2) { create model, options}
246
+
247
+ it 'returns items' do
248
+ return_result = case scope_action
249
+ when :all_records
250
+ [item_1, item_2]
251
+ when :ordered
252
+ [item_2, item_1]
253
+ when :reverse_ordered
254
+ [item_1, item_2]
255
+ when :limited
256
+ [item_1]
257
+ when :selected
258
+ [item_1, item_2]
259
+ when :grouped
260
+ { item_1.send(group_by) => 2 }
261
+ when :having_grouped
262
+ { item_1.try(group_by) => 1, item_2.try(group_by) => 1 }
263
+ when :offsetted
264
+ [item_2]
265
+ end
266
+
267
+ result = model.send(scope_action)
268
+ if scope_action == :grouped || scope_action == :having_grouped
269
+ result = result.count
270
+ end
271
+
272
+ expect(result).to eq return_result
273
+ end
274
+ end
275
+ end
276
+
277
+ RSpec.shared_examples 'selector all with update' do |field, options = {}|
278
+ let!(:item_1) { create model }
279
+ let!(:item_2) { create model }
280
+
281
+ describe ".all after update" do
282
+ it 'returns item' do
283
+ item_2.update(options)
284
+ expect(model.where(id: item_2.id)[0].send(field)).to eq item_2.send(field)
285
+ end
286
+ end
287
+
288
+ end
289
+
290
+ RSpec.shared_examples 'find selector' do
291
+ let!(:item_1) { create model }
292
+ let!(:item_2) { create model }
293
+
294
+ describe ".find" do
295
+ it 'returns item' do
296
+ expect(model.send(:find, item_2.id)).to eq item_2
297
+ end
298
+ end
299
+
300
+ describe ".find array" do
301
+ it 'returns items' do
302
+ expect(model.send(:find, [item_1.id, item_2.id])).to eq [item_1, item_2]
303
+ end
304
+ end
305
+
306
+ describe ".find_in_batches" do
307
+ it 'returns all items' do
308
+ model.send(:find_in_batches) do |batch|
309
+ expect(batch).to eq [item_1, item_2]
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+
316
+ RSpec.shared_examples 'having selector' do
317
+ let!(:item_1) { create model }
318
+ let!(:item_2) { create model }
319
+
320
+ describe ".find" do
321
+ it 'returns item' do
322
+ expect(model.send(:find, item_2.id)).to eq item_2
323
+ end
324
+ end
325
+
326
+ describe ".find array" do
327
+ it 'returns items' do
328
+ expect(model.send(:find, [item_1.id, item_2.id])).to eq [item_1, item_2]
329
+ end
330
+ end
331
+
332
+ describe ".find_in_batches" do
333
+ it 'returns all items' do
334
+ model.send(:find_in_batches) do |batch|
335
+ expect(batch).to eq [item_1, item_2]
336
+ end
337
+ end
338
+ end
339
+ end
340
+
341
+ RSpec.shared_examples 'distinct selector' do |field|
342
+ let!(:item_1) { create model, field => 1 }
343
+ let!(:item_2) { create model, field => 2 }
344
+ let!(:item_3) { create model, field => 2 }
345
+ let!(:item_4) { create model, field => 3 }
346
+
347
+ it "returns unique values" do
348
+ expect(model.select(field).distinct.map(&field).map(&:to_i).sort).to eq([1, 2, 3])
349
+ end
350
+ end
351
+
352
+ RSpec.shared_examples 'eager_load selector' do |association|
353
+ let!(:main_item) { create model }
354
+
355
+ before do
356
+ main_item.send(association).create!
357
+ @sub_item1 = main_item.send(association).order(id: :desc)[0]
358
+ main_item.send(association).create!
359
+ @sub_item2 = main_item.send(association).order(id: :desc)[0]
360
+ main_item.send(association).create!
361
+ @sub_item3 = main_item.send(association).order(id: :desc)[0]
362
+ end
363
+
364
+ it "loads the associated items" do
365
+ expect(model.eager_load(association).where(id: main_item.id)[0].send(association).to_a).to eq([@sub_item1, @sub_item2, @sub_item3])
366
+ end
367
+ end
368
+
369
+ RSpec.shared_examples 'extending scope' do
370
+ let!(:item_1) { create model }
371
+ let!(:item_2) { create model }
372
+ let!(:item_3) { create model }
373
+ let!(:item_4) { create model }
374
+ let!(:item_5) { create model }
375
+ let!(:item_6) { create model }
376
+
377
+ it "allows extending a scope" do
378
+ scope = model.all.extending do
379
+ def page(number)
380
+ per_page = 4
381
+ limit(per_page).offset((number - 1) * per_page)
382
+ end
383
+ end
384
+ expect(scope.page(2).to_a).to eq([item_5, item_6])
385
+ end
386
+ end
387
+
388
+ RSpec.shared_examples 'none relation' do |association|
389
+ it "returns an empty active record relation" do
390
+ expect(model.none.to_a).to eq([])
391
+ end
392
+ end
393
+
394
+ RSpec.shared_examples 'preload selector' do |association|
395
+ let!(:main_item) { create model }
396
+
397
+ before do
398
+ main_item.send(association).create!
399
+ @sub_item1 = main_item.send(association).order(id: :desc)[0]
400
+ main_item.send(association).create!
401
+ @sub_item2 = main_item.send(association).order(id: :desc)[0]
402
+ main_item.send(association).create!
403
+ @sub_item3 = main_item.send(association).order(id: :desc)[0]
404
+ end
405
+
406
+ it "loads the associated items" do
407
+ expect(model.preload(association).where(id: main_item.id)[0].send(association).to_a).to eq([@sub_item1, @sub_item2, @sub_item3])
408
+ end
409
+ end
410
+
411
+ RSpec.shared_examples 'references selector' do |association|
412
+ let!(:main_item) { create model }
413
+
414
+ before do
415
+ main_item.send(association).create!(created_at: 1.week.ago)
416
+ @sub_item1 = main_item.send(association).order(id: :desc)[0]
417
+ main_item.send(association).create!(created_at: 2.weeks.ago)
418
+ @sub_item2 = main_item.send(association).order(id: :desc)[0]
419
+ main_item.send(association).create!(created_at: 3.weeks.ago)
420
+ @sub_item3 = main_item.send(association).order(id: :desc)[0]
421
+ end
422
+
423
+ it "loads the associated items with an SQL query fragment" do
424
+ expect(model.includes(association).where("#{association}.created_at >= ?", 16.days.ago).references(association)[0].send(association)).to eq([@sub_item1, @sub_item2])
425
+ end
426
+ end
427
+
428
+ RSpec.shared_examples 'reverse_order selector' do
429
+ let!(:item1) { create model, name: 'name1' }
430
+ let!(:item2) { create model, name: 'name2' }
431
+ let!(:item3) { create model, name: 'name3' }
432
+
433
+ it "reverses the order of returned items" do
434
+ expect(model.order(:name).reverse_order).to eq([item3, item2, item1])
435
+ end
436
+ end
437
+
438
+ RSpec.shared_examples 'find_by selector' do |field, value1, value2|
439
+ let!(:item1) { create model, field => value1 }
440
+ let!(:item2) { create model, field => value2 }
441
+
442
+ it "returns the item with the correct value" do
443
+ expect(model.find_by(field => value1)).to eq(item1)
444
+ end
445
+ end
446
+
447
+ RSpec.shared_examples 'range conditions' do
448
+ let!(:item1) { create model, created_at: 1.day.ago }
449
+ let!(:item2) { create model, created_at: 2.days.ago }
450
+ let!(:item3) { create model, created_at: 4.days.ago }
451
+
452
+ it "returns the item with the correct value" do
453
+ expect(model.where(created_at: 3.days.ago..DateTime.now)).to eq([item1, item2])
454
+ end
455
+ end
456
+
457
+ RSpec.shared_examples 'subset conditions' do
458
+ let!(:item1) { create model, name: 'name1' }
459
+ let!(:item2) { create model, name: 'name2' }
460
+ let!(:item3) { create model, name: 'name3' }
461
+ let!(:item4) { create model, name: 'name4' }
462
+
463
+ it "returns the items with the correct values" do
464
+ expect(model.where(name: ['name1', 'name2', 'name4'])).to eq([item1, item2, item4])
465
+ end
466
+ end
467
+
468
+ RSpec.shared_examples 'find_or_create_by selector' do |field, value|
469
+ it "creates a new object when it doesn't exist" do
470
+ expect(model.find_or_create_by(field => value).send(field)).to eq(value)
471
+ end
472
+
473
+ it "returns an existing object" do
474
+ item1 = create(model, field => value)
475
+ expect(model.find_or_create_by(field => value)).to eq(item1)
476
+ end
477
+ end
478
+
479
+ RSpec.shared_examples 'find_or_create_by! selector' do |field, value|
480
+ it "creates a new object when it doesn't exist" do
481
+ expect(model.find_or_create_by!(field => value).send(field)).to eq(value)
482
+ end
483
+
484
+ it "returns an existing object" do
485
+ item1 = create(model, field => value)
486
+ expect(model.find_or_create_by!(field => value)).to eq(item1)
487
+ end
488
+ end
489
+
490
+ RSpec.shared_examples 'find_or_initialize_by selector' do |field, value|
491
+ it "initializes a new object when it doesn't exist" do
492
+ expect(model.find_or_initialize_by(field => value).attributes).to eq(model.new(field => value).attributes)
493
+ end
494
+
495
+ it "returns an existing object" do
496
+ item1 = create(model, field => value)
497
+ expect(model.find_or_initialize_by(field => value)).to eq(item1)
498
+ end
499
+ end
500
+
501
+ RSpec.shared_examples 'select_all selector' do
502
+ let!(:item) { create model }
503
+ it "returns an array of hashes with results from the database" do
504
+ expect(model.connection.select_all("SELECT * FROM #{model.table_name} WHERE id = #{item.id}").to_a[0].slice('id', 'name', 'description')).to eq({"id" => item.id, "name" => item.name, "description" => item.description})
505
+ end
506
+ end
507
+
508
+ RSpec.shared_examples 'pluck selector' do |field|
509
+ let!(:item1) { create model, field => 'val1' }
510
+ let!(:item2) { create model, field => 'val2' }
511
+ let!(:item3) { create model, field => 'val3' }
512
+
513
+ it "returns an array of values for the field" do
514
+ expect(model.pluck(field)).to eq(['val1', 'val2', 'val3'])
515
+ end
516
+ end
517
+
518
+ RSpec.shared_examples 'ids selector' do
519
+ let!(:item1) { create model }
520
+ let!(:item2) { create model }
521
+ let!(:item3) { create model }
522
+
523
+ it "returns an array of values for the field" do
524
+ expect(model.ids).to eq([item1.id, item2.id, item3.id])
525
+ end
526
+ end
527
+
528
+ RSpec.shared_examples 'exists? selector' do |field, value, other_value|
529
+ let!(:item) { create model, field => value }
530
+
531
+ it "returns true when object exists" do
532
+ expect(model.where(field => value).exists?).to eq(true)
533
+ end
534
+
535
+ it "returns false when object doesn't exist" do
536
+ expect(model.where(field => other_value).exists?).to eq(false)
537
+ end
538
+ end
539
+
540
+ RSpec.shared_examples 'minimum selector' do |field, values|
541
+ it "returns the minimal value for selected field" do
542
+ minimum = values.min
543
+ values.each do |value|
544
+ create(model, field => value)
545
+ end
546
+ expect(model.minimum(field)).to eq(minimum)
547
+ end
548
+ end
549
+
550
+ RSpec.shared_examples 'raw sql selector' do |field, value|
551
+ it "returns the expected results" do
552
+ create(model, field => value)
553
+ sql = "SELECT * FROM #{model.table_name} ORDER BY #{model.table_name}.id DESC FETCH NEXT 1 ROWS ONLY"
554
+ results = ActiveRecord::Base.connection.execute(sql)
555
+ expect(results[0][field.to_s]).to eq(value)
556
+ end
557
+ end
558
+ end
559
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_tester
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleksandar Zoric
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Test ActiveRecord methods against specific models in your database.
56
+ email:
57
+ - aleksandroz.mail@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - database_tester.gemspec
72
+ - lib/database/tester.rb
73
+ - lib/database/tester/version.rb
74
+ homepage: http://aleksandroz.com
75
+ licenses:
76
+ - MIT
77
+ metadata:
78
+ allowed_push_host: https://rubygems.org
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.5.1
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Testing database methods made easy.
99
+ test_files: []