fixture_bot 0.1.0 → 0.3.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 +4 -4
- data/README.md +71 -148
- data/lib/fixture_bot/fixture_creator.rb +6 -3
- data/lib/fixture_bot/version.rb +1 -1
- data/lib/fixture_bot.rb +23 -20
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55e1c0684eed79ec6b077d7e9459afec9aa884e47c999963aeef4f4914a3bee5
|
4
|
+
data.tar.gz: 8b856c48c24ec4567a5ed2cf5cdeda4d125ff94aad321822d65264319a501bf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4849de201e6c43a1b84d8043fe295cb137cbbc97d81eba1a4558f6c65bc930b00158842742af182fb1296ffbd6b8433715726cb7b97d172f8e68a3abf4e1ed3
|
7
|
+
data.tar.gz: daf184bd8d5e207a3b81bd1d5d0f8c621080de4dad20e5b33eb8a2ef7cdc5e5dfa7b0f28d4d079ba5cce71193247b4c7dce488dc170ef2e32a45f46be1722b52
|
data/README.md
CHANGED
@@ -1,195 +1,105 @@
|
|
1
|
-
#
|
1
|
+
# `fixture_bot`
|
2
2
|
|
3
|
-
[
|
4
|
-
[](https://codeclimate.com/github/fnando/factory_bot-preload)
|
5
|
-
[](https://codeclimate.com/github/fnando/factory_bot-preload/coverage)
|
6
|
-
[](https://rubygems.org/gems/factory_bot-preload)
|
7
|
-
[](https://rubygems.org/gems/factory_bot-preload)
|
3
|
+
Improve the performance of your tests, as factories generate and insert data into the database every time, it can be slow. See [benchmarks](#Benchmarks).
|
8
4
|
|
9
|
-
|
5
|
+
Problems using [Rails fixtures](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html):
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
- No data validation
|
8
|
+
- Long loading of fixtures (for one or all tests - equally long)
|
9
|
+
- YML format (no reuse code)
|
10
|
+
- heavy support for fixtures and factories together
|
14
11
|
|
15
12
|
### Installation
|
16
13
|
|
17
|
-
Add both FB and FBP to your Gemfile:
|
18
|
-
|
19
14
|
```ruby
|
20
|
-
|
21
|
-
|
22
|
-
gem "rails"
|
23
|
-
|
24
|
-
group :test, :development do
|
15
|
+
group :test do
|
25
16
|
gem "factory_bot"
|
26
17
|
gem "fixture_bot", require: false
|
27
18
|
end
|
28
19
|
```
|
29
20
|
|
30
|
-
Notice that adding `require: false` is important; otherwise you won't be able to
|
31
|
-
run commands such as `rails db:test:prepare`.
|
32
|
-
|
33
|
-
### RSpec Setup
|
34
|
-
|
35
|
-
On your `spec/spec_helper.rb` file, make sure that transactional fixtures are
|
36
|
-
enabled. Here's is my file without all those RSpec comments:
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
ENV["RAILS_ENV"] ||= "test"
|
40
|
-
require File.expand_path("../../config/environment", __FILE__)
|
41
|
-
require "rspec/rails"
|
42
|
-
|
43
|
-
# First, load fixture_bot/preload.
|
44
|
-
require "fixture_bot/preload"
|
45
|
-
|
46
|
-
# Then load your factories
|
47
|
-
Dir[Rails.root.join("spec/support/factories/**/*.rb")].each do |file|
|
48
|
-
require file
|
49
|
-
end
|
50
|
-
|
51
|
-
RSpec.configure do |config|
|
52
|
-
config.use_transactional_fixtures = true
|
53
|
-
config.mock_with :rspec
|
54
|
-
end
|
55
|
-
```
|
56
|
-
|
57
|
-
### Minitest Setup
|
58
|
-
|
59
|
-
On your `test/test_helper.rb` file, make sure that transaction fixtures are
|
60
|
-
enabled. Here's what your file may look like:
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
ENV["RAILS_ENV"] ||= "test"
|
64
|
-
require_relative "../config/environment"
|
65
|
-
require "rails/test_help"
|
66
|
-
|
67
|
-
module ActiveSupport
|
68
|
-
class TestCase
|
69
|
-
self.use_instantiated_fixtures = true
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# First, load fixture_bot/preload.
|
74
|
-
require "fixture_bot/preload"
|
75
|
-
|
76
|
-
# Then load your factories.
|
77
|
-
Dir["./test/support/factories/**/*.rb"].each do |file|
|
78
|
-
require file
|
79
|
-
end
|
80
|
-
|
81
|
-
# Finally, setup minitest.
|
82
|
-
# Your factories won't behave correctly unless you
|
83
|
-
# call `FixtureBot.minitest` after loading them.
|
84
|
-
FixtureBot.minitest
|
85
|
-
```
|
86
|
-
|
87
21
|
### Usage
|
88
22
|
|
89
|
-
|
90
|
-
`test/test_helper.rb` or `spec/spec_helper.rb`) You may have something like
|
91
|
-
this:
|
23
|
+
To define your fixture in factories, use the `preload` method
|
92
24
|
|
93
25
|
```ruby
|
94
26
|
FactoryBot.define do
|
95
27
|
factory :user do
|
96
28
|
name "John Doe"
|
97
29
|
sequence(:email) {|n| "john#{n}@example.org" }
|
98
|
-
sequence(:username) {|n| "john#{n}" }
|
99
|
-
password "test"
|
100
|
-
password_confirmation "test"
|
101
30
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
31
|
+
|
32
|
+
preload(:users) do
|
33
|
+
fixture_with_id(:first) { create(:user, id: 1) }
|
34
|
+
fixture(:john) { create(:user) }
|
35
|
+
fixture(:with_gmail) { create(:user, email: "email@gmail.com") }
|
106
36
|
end
|
107
37
|
end
|
108
38
|
```
|
109
39
|
|
110
|
-
To define your preloadable factories, just use the `preload` method:
|
111
|
-
|
112
40
|
```ruby
|
113
41
|
FactoryBot.define do
|
114
|
-
factory :user do
|
115
|
-
name "John Doe"
|
116
|
-
sequence(:email) {|n| "john#{n}@example.org" }
|
117
|
-
sequence(:username) {|n| "john#{n}" }
|
118
|
-
password "test"
|
119
|
-
password_confirmation "test"
|
120
|
-
end
|
121
|
-
|
122
42
|
factory :projects do
|
123
43
|
name "My Project"
|
124
|
-
|
44
|
+
user { users(:with_gmail) }
|
125
45
|
end
|
126
46
|
|
127
|
-
preload do
|
128
|
-
fixture(:john) { create(:user) }
|
47
|
+
preload(:users) do
|
129
48
|
fixture(:myapp) { create(:project, user: users(:john)) }
|
130
49
|
end
|
131
50
|
end
|
132
51
|
```
|
133
52
|
|
134
|
-
|
53
|
+
|
54
|
+
### RSpec usage
|
135
55
|
|
136
56
|
```ruby
|
137
|
-
|
138
|
-
|
139
|
-
|
57
|
+
require "spec_helper"
|
58
|
+
|
59
|
+
describe User do
|
60
|
+
let(:user) { users(:john) }
|
61
|
+
|
62
|
+
it "returns john's record" do
|
63
|
+
expect(users(:john)).to be_a User
|
140
64
|
end
|
141
65
|
|
142
|
-
|
143
|
-
|
144
|
-
user { users(:john) }
|
66
|
+
it "returns myapp's record" do
|
67
|
+
expect(projects(:myapp).user).to eq users(:john)
|
145
68
|
end
|
146
69
|
|
147
|
-
|
148
|
-
|
149
|
-
fixture(:myapp) { create(:project, user: users(:john)) }
|
70
|
+
it "each call fixture return new object" do
|
71
|
+
expect(user.object_id).not_to eq users(:john).object_id
|
150
72
|
end
|
151
73
|
end
|
152
74
|
```
|
153
75
|
|
76
|
+
### RSpec Setup
|
154
77
|
|
155
|
-
|
78
|
+
On your `spec/support/factory_bot.rb` file
|
156
79
|
|
157
80
|
```ruby
|
158
|
-
|
159
|
-
|
81
|
+
require "fixture_bot" # the order is important, it must be before loaded factories
|
82
|
+
require "factory_bot_rails"
|
160
83
|
|
161
|
-
|
162
|
-
|
163
|
-
end
|
84
|
+
FactoryBot::SyntaxRunner.class_eval do
|
85
|
+
include RSpec::Mocks::ExampleMethods
|
164
86
|
end
|
165
|
-
```
|
166
|
-
|
167
|
-
```ruby
|
168
|
-
FactoryBot.define do
|
169
|
-
factory :user do
|
170
|
-
# ...
|
171
|
-
end
|
172
87
|
|
173
|
-
|
174
|
-
|
175
|
-
fixture_stub_const(:admin, :ADMIN_ID) { create(:user) }
|
176
|
-
end
|
88
|
+
RSpec.configure do |config|
|
89
|
+
config.include FactoryBot::Syntax::Methods
|
177
90
|
end
|
178
91
|
```
|
179
92
|
|
180
|
-
|
181
|
-
describe User do
|
182
|
-
let(:admin) { users(:admin) }
|
183
|
-
|
184
|
-
it "admin can edit article" do
|
185
|
-
expect(admin).to be_can_edit_article
|
186
|
-
end
|
187
|
-
end
|
188
|
-
````
|
93
|
+
### Minitest Setup
|
189
94
|
|
95
|
+
On your `test/test_helper.rb` file, make sure that transaction fixtures are
|
96
|
+
enabled. Here's what your file may look like
|
190
97
|
|
191
|
-
|
192
|
-
|
98
|
+
```ruby
|
99
|
+
# First, load fixture_bot
|
100
|
+
require "fixture_bot"
|
101
|
+
FixtureBot.minitest
|
102
|
+
```
|
193
103
|
|
194
104
|
```ruby
|
195
105
|
require "test_helper"
|
@@ -205,25 +115,38 @@ class UserTest < ActiveSupport::TestCase
|
|
205
115
|
end
|
206
116
|
```
|
207
117
|
|
208
|
-
|
209
|
-
|
118
|
+
## Callbacks
|
210
119
|
```ruby
|
211
|
-
|
120
|
+
FixtureBot.after_load_fixtures do
|
121
|
+
# code uses fixtures
|
122
|
+
end
|
123
|
+
```
|
212
124
|
|
213
|
-
|
214
|
-
|
125
|
+
## Benchmarks
|
126
|
+
#### factories vs fixtures
|
215
127
|
|
216
|
-
|
217
|
-
|
218
|
-
|
128
|
+
```ruby
|
129
|
+
# simple model with 10 fields
|
130
|
+
Benchmark.ips do |x|
|
131
|
+
x.report("fixture") { brands(:lux) }. # fixture: 4666.2 i/s
|
132
|
+
x.report("factory") { create(:brand) } # factory: 1077.8 i/s - 4.33x slower
|
133
|
+
x.compare!
|
134
|
+
end
|
219
135
|
|
220
|
-
|
221
|
-
|
222
|
-
|
136
|
+
# user model with 40+ fields, 1 association
|
137
|
+
Benchmark.ips do |x|
|
138
|
+
x.report("fixture") { users(:with_post_index)} # fixture: 3395.4 i/s
|
139
|
+
x.report("factory") { create(:user) } # factory: 159.6 i/s - 21.27x slower
|
140
|
+
x.compare!
|
223
141
|
end
|
224
|
-
```
|
225
142
|
|
226
|
-
|
143
|
+
# product model with 40+ fields, 5+ associations
|
144
|
+
Benchmark.ips do |x|
|
145
|
+
x.report("fixture") { products(:available) } # fixture: 3564.3 i/s
|
146
|
+
x.report("factory") { create(:product, :available) } # factory: 67.7 i/s - 52.68x slower
|
147
|
+
x.compare!
|
148
|
+
end
|
149
|
+
```
|
227
150
|
|
228
151
|
## License
|
229
152
|
|
@@ -62,9 +62,12 @@ module FixtureBot
|
|
62
62
|
|
63
63
|
private def fixture_with_id(table, name, &)
|
64
64
|
_, record_id = fixture(table, name, &)
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
|
66
|
+
if ::ActiveRecord::Base.connection.respond_to?(:postgresql_version)
|
67
|
+
::ActiveRecord::Base.connection.execute <<~SQL
|
68
|
+
SELECT setval(pg_get_serial_sequence('#{table}', 'id'), GREATEST(#{record_id}, nextval(pg_get_serial_sequence('#{table}', 'id'))))
|
69
|
+
SQL
|
70
|
+
end
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
data/lib/fixture_bot/version.rb
CHANGED
data/lib/fixture_bot.rb
CHANGED
@@ -21,12 +21,12 @@ module FixtureBot
|
|
21
21
|
def run
|
22
22
|
cached_mtime, cached_record_ids = cached_fixtures
|
23
23
|
if cached_mtime && cached_mtime == max_mtime_fixtures
|
24
|
-
|
24
|
+
colored_output "Cache load fixtures"
|
25
25
|
|
26
|
-
FixtureBot::FixtureCreator.record_ids = Marshal.load(cached_record_ids)
|
26
|
+
FixtureBot::FixtureCreator.record_ids = Marshal.load(Base64.decode64(cached_record_ids))
|
27
27
|
define_fixture_helpers
|
28
28
|
else
|
29
|
-
|
29
|
+
colored_output "Full load fixtures"
|
30
30
|
|
31
31
|
clean_db
|
32
32
|
load_models
|
@@ -50,15 +50,15 @@ module FixtureBot
|
|
50
50
|
|
51
51
|
def cached_fixtures
|
52
52
|
connection.query(<<-SQL).first
|
53
|
-
CREATE TABLE IF NOT EXISTS
|
54
|
-
SELECT fixtures_time, fixtures_dump FROM
|
53
|
+
CREATE TABLE IF NOT EXISTS __fixture_bot_cache_v1(fixtures_time timestamptz, fixtures_dump bytea);
|
54
|
+
SELECT fixtures_time, fixtures_dump FROM __fixture_bot_cache_v1
|
55
55
|
SQL
|
56
56
|
end
|
57
57
|
|
58
58
|
def caching_max_mtime_fixtures(dump_record_ids)
|
59
|
+
truncate_tables(["__fixture_bot_cache_v1"])
|
59
60
|
connection.execute <<-SQL
|
60
|
-
|
61
|
-
INSERT INTO __factory_bot_preload_cache_v1 VALUES ('#{max_mtime_fixtures.iso8601(6)}', '#{connection.raw_connection.escape_bytea(dump_record_ids)}')
|
61
|
+
INSERT INTO __fixture_bot_cache_v1 VALUES ('#{max_mtime_fixtures.iso8601(6)}', '#{Base64.encode64(dump_record_ids)}')
|
62
62
|
SQL
|
63
63
|
end
|
64
64
|
|
@@ -80,24 +80,27 @@ module FixtureBot
|
|
80
80
|
].freeze
|
81
81
|
|
82
82
|
def clean_db
|
83
|
-
tables = connection.tables - RESERVED_TABLES
|
84
|
-
|
85
|
-
query =
|
86
|
-
case connection.adapter_name
|
87
|
-
when "SQLite"
|
88
|
-
tables.map { |table| "DELETE FROM #{connection.quote_table_name(table)}" }.join(";")
|
89
|
-
when "PostgreSQL"
|
90
|
-
"TRUNCATE TABLE #{tables.map { |table| connection.quote_table_name(table) }.join(',')} RESTART IDENTITY CASCADE"
|
91
|
-
else
|
92
|
-
"TRUNCATE TABLE #{tables.map { |table| connection.quote_table_name(table) }.join(',')}"
|
93
|
-
end
|
94
|
-
|
95
83
|
connection.disable_referential_integrity do
|
96
|
-
connection.execute(
|
84
|
+
connection.execute(truncate_tables(connection.tables - RESERVED_TABLES))
|
97
85
|
end
|
98
86
|
end
|
99
87
|
|
100
88
|
def connection
|
101
89
|
::ActiveRecord::Base.connection
|
102
90
|
end
|
91
|
+
|
92
|
+
def truncate_tables(tables)
|
93
|
+
case connection.adapter_name
|
94
|
+
when "SQLite"
|
95
|
+
tables.map { |table| "DELETE FROM #{connection.quote_table_name(table)}" }.join(";")
|
96
|
+
when "PostgreSQL"
|
97
|
+
"TRUNCATE TABLE #{tables.map { |table| connection.quote_table_name(table) }.join(',')} RESTART IDENTITY CASCADE"
|
98
|
+
else
|
99
|
+
"TRUNCATE TABLE #{tables.map { |table| connection.quote_table_name(table) }.join(',')}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def colored_output(text)
|
104
|
+
puts "\e[33m#{text}\e[0m"
|
105
|
+
end
|
103
106
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixture_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ermolaev Andrey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|