fixture_bot 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Code Climate](https://codeclimate.com/github/fnando/factory_bot-preload/badges/gpa.svg)](https://codeclimate.com/github/fnando/factory_bot-preload)
|
5
|
-
[![Test Coverage](https://codeclimate.com/github/fnando/factory_bot-preload/badges/coverage.svg)](https://codeclimate.com/github/fnando/factory_bot-preload/coverage)
|
6
|
-
[![Gem](https://img.shields.io/gem/v/factory_bot-preload.svg)](https://rubygems.org/gems/factory_bot-preload)
|
7
|
-
[![Gem](https://img.shields.io/gem/dt/factory_bot-preload.svg)](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
|