fixture_bot 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 882daa77b1a9840f2179c979b5bd51e09782e7ef011d28dc761bbbdd65789942
4
- data.tar.gz: 0c8957cc5ff3f0c412ae7e9d85acc41baf407b7090de7f2e55450e77573ae13e
3
+ metadata.gz: 8eb18affd0227587d706dcbd9f598b1118f175cba7f0b7abd29ddf57618a42af
4
+ data.tar.gz: 23f67409d1c4bde7b00e757cacfbfc34885954915e492f4730c8c3c5dd50050b
5
5
  SHA512:
6
- metadata.gz: e4c795d92c941773f0af08ffa32d289a0d6b7ae2421cfc3cb6934f2c94724d53c64078d0b179e25761ec534a8a5a96b67333146eb4bdbb0ee926a0a6ba2a85ec
7
- data.tar.gz: 9869573d964e08fb7f7632f3a624688f48a066f325a87baa51ab789120cc286d8f165001f0954817aeaf4151aaec6e803c1e7247b288dbc13824a103fb94fb6b
6
+ metadata.gz: 726a157de2ec6dd416daeba4783023754cce5ebc47982364a82ed0cf1962a34b4e5ac66f528fc335650a5c2f9258a5fba11d3e4ade6d1787c1b4632d03c9cd08
7
+ data.tar.gz: a724d0bf57b9c2ae3afea43d65d38b626394807cd8494df0c36f1f23417ad6bca42c92a4152447f0aa679dbaac1191256b59665b396d3da50ce32590c61af41e
data/README.md CHANGED
@@ -1,195 +1,105 @@
1
- # factory_bot-preload
1
+ # `fixture_bot`
2
2
 
3
- [![Travis-CI](https://travis-ci.org/fnando/factory_bot-preload.svg)](https://travis-ci.org/fnando/factory_bot-preload)
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
- We all love Rails fixtures because they're fast, but we hate to deal with YAML/CSV/SQL files. Here enters [factory_bot](https://rubygems.org/gems/factory_bot) (FB).
5
+ Problems using [Rails fixtures](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html):
10
6
 
11
- Now, you can easily create records by using predefined factories. The problem is that hitting the database everytime to create records is pretty slow. And believe me, you'll feel the pain when you have lots of tests/specs.
12
-
13
- So here enters Factory Bot Preload (FBP). You can define which factories will be preloaded, so you don't have to recreate it every time (that will work for 99.37% of the time, according to statistics I just made up).
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
- source "https://rubygems.org"
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
- Create your factories and load it from your setup file (either
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
- factory :projects do
104
- name "My Project"
105
- association :user
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
- association :user
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
- You can also use preloaded factories on factory definitions.
53
+
54
+ ### RSpec usage
135
55
 
136
56
  ```ruby
137
- FactoryBot.define do
138
- factory :user do
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
- factory :projects do
143
- name "My Project"
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
- preload do
148
- fixture(:john) { create(:user) }
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
- If you need to create records with specifying id, use `fixture_stub_const`, it stub const in model to next value from generated primary key
78
+ On your `spec/support/factory_bot.rb` file
156
79
 
157
80
  ```ruby
158
- class User
159
- ADMIN_ID = 10
81
+ require "fixture_bot" # the order is important, it must be before loaded factories
82
+ require "factory_bot_rails"
160
83
 
161
- def can_edit_article?
162
- id == ADMIN_ID
163
- end
84
+ FactoryBot::SyntaxRunner.class_eval do
85
+ include RSpec::Mocks::ExampleMethods
164
86
  end
165
- ```
166
87
 
167
- ```ruby
168
- FactoryBot.define do
169
- factory :user do
170
- # ...
171
- end
172
-
173
- preload do
174
- fixture(:john) { create(:user) }
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
- ```ruby
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
- Like Rails fixtures, FBP will define methods for each model. You can use it on
192
- your examples and alike.
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,42 @@ class UserTest < ActiveSupport::TestCase
205
115
  end
206
116
  ```
207
117
 
208
- Or if you're using RSpec:
209
-
118
+ ## Callbacks
210
119
  ```ruby
211
- require "spec_helper"
120
+ FixtureBot.after_load_fixtures do
121
+ # code uses fixtures
122
+ end
123
+ ```
212
124
 
213
- describe User do
214
- let(:user) { users(:john) }
125
+ ## Benchmarks
126
+ #### factories vs fixtures
215
127
 
216
- it "returns john's record" do
217
- users(:john).should be_an(User)
218
- end
128
+ simple model with 10 fields
129
+ ```ruby
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
135
+ ```
219
136
 
220
- it "returns myapp's record" do
221
- projects(:myapp).user.should == users(:john)
222
- end
137
+ user model with 40+ fields, 1 association
138
+ ```ruby
139
+ Benchmark.ips do |x|
140
+ x.report("fixture") { users(:with_post_index)} # fixture: 3395.4 i/s
141
+ x.report("factory") { create(:user) } # factory: 159.6 i/s - 21.27x slower
142
+ x.compare!
223
143
  end
224
144
  ```
225
145
 
226
- That's it!
146
+ product model with 40+ fields, 5+ associations
147
+ ```ruby
148
+ Benchmark.ips do |x|
149
+ x.report("fixture") { products(:available) } # fixture: 3564.3 i/s
150
+ x.report("factory") { create(:product, :available) } # factory: 67.7 i/s - 52.68x slower
151
+ x.compare!
152
+ end
153
+ ```
227
154
 
228
155
  ## License
229
156
 
@@ -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
- ::ActiveRecord::Base.connection.execute <<~SQL
66
- SELECT setval(pg_get_serial_sequence('#{table}', 'id'), GREATEST(#{record_id}, nextval(pg_get_serial_sequence('#{table}', 'id'))))
67
- SQL
65
+
66
+ if ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FixtureBot
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
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
- puts "Cache load fixtures".yellow
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
- puts "Full load fixtures".yellow
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 __factory_bot_preload_cache_v1(fixtures_time timestamptz, fixtures_dump bytea);
54
- SELECT fixtures_time, fixtures_dump FROM __factory_bot_preload_cache_v1
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
- TRUNCATE TABLE __factory_bot_preload_cache_v1;
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(query)
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.1.0
4
+ version: 0.2.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-09-29 00:00:00.000000000 Z
11
+ date: 2024-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord