database_tester 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +148 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/database_tester.gemspec +35 -0
- data/lib/database/tester/version.rb +5 -0
- data/lib/database/tester.rb +559 -0
- metadata +99 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
|
+
[](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
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,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,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: []
|