test_data 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/Gemfile.lock +15 -15
  4. data/LICENSE.txt +1 -6
  5. data/README.md +652 -276
  6. data/example/Gemfile.lock +73 -73
  7. data/example/test/integration/better_mode_switching_demo_test.rb +4 -0
  8. data/example/test/integration/parallel_boops_with_fixtures_test.rb +2 -2
  9. data/example/test/integration/parallel_boops_without_fixtures_test.rb +2 -2
  10. data/example/test/integration/rails_fixtures_double_load_test.rb +10 -0
  11. data/example/test/integration/rails_fixtures_override_test.rb +127 -0
  12. data/example/test/integration/transaction_committing_boops_test.rb +14 -3
  13. data/example/test/test_helper.rb +2 -2
  14. data/lib/generators/test_data/cable_yaml_generator.rb +18 -0
  15. data/lib/generators/test_data/database_yaml_generator.rb +2 -3
  16. data/lib/generators/test_data/environment_file_generator.rb +3 -0
  17. data/lib/generators/test_data/initializer_generator.rb +7 -0
  18. data/lib/generators/test_data/secrets_yaml_generator.rb +19 -0
  19. data/lib/generators/test_data/webpacker_yaml_generator.rb +3 -2
  20. data/lib/test_data.rb +5 -0
  21. data/lib/test_data/active_record_ext.rb +11 -0
  22. data/lib/test_data/config.rb +14 -2
  23. data/lib/test_data/configurators.rb +2 -0
  24. data/lib/test_data/configurators/cable_yaml.rb +25 -0
  25. data/lib/test_data/configurators/environment_file.rb +3 -2
  26. data/lib/test_data/configurators/initializer.rb +3 -2
  27. data/lib/test_data/configurators/secrets_yaml.rb +25 -0
  28. data/lib/test_data/configurators/webpacker_yaml.rb +4 -3
  29. data/lib/test_data/dumps_database.rb +24 -1
  30. data/lib/test_data/generator_support.rb +3 -0
  31. data/lib/test_data/loads_database_dumps.rb +1 -1
  32. data/lib/test_data/log.rb +19 -1
  33. data/lib/test_data/rake.rb +16 -6
  34. data/lib/test_data/statistics.rb +6 -1
  35. data/lib/test_data/transactional_data_loader.rb +156 -46
  36. data/lib/test_data/version.rb +1 -1
  37. data/script/test +11 -0
  38. data/test_data.gemspec +1 -1
  39. metadata +11 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb3ddac1f4153d6b3995975a7b3016089c0f64ef783b45af3b21d5a2ea00f671
4
- data.tar.gz: 0d45ac53789a0015c88f6d3f8cc545409c4c42fbd112f63edb1489cc5d603c7c
3
+ metadata.gz: 492abd0bddc1329c073d45cb984dd60aa374f3520c6d3362bec34ece165f0407
4
+ data.tar.gz: efee863beeba57aa27e98d8b833ca0398d022baaa710b8a741d8da3995a71dce
5
5
  SHA512:
6
- metadata.gz: 7368531e5dcc41910350809eb2d5a36763f395a749f69dbe85579792cca513fbe5663a0c02c2595009617a63d274caba073572a854cb3f334a07b4b57ce98ff8
7
- data.tar.gz: 358f87044733160c2d4ee49b6e8ec1b422cc08e6aa4f0a12a7b47f190899b67ea707159e5cc2e66c307934ad4c1315c45fcbde4666eeb5497b98868afbae31ee
6
+ metadata.gz: 1796bcb024853380c7a387f5f69d8b564ca78c4318fbf0a3c526546c5000fb5f676cc846a0541443c5d53e664f3b74d1970df4526d12b7f931209e9d78cdb684
7
+ data.tar.gz: b77b0c1a24a806ac817d516173a6f68b9e7d63ca088bae6cc32de41c688df0c296a1eedf7e7bde81ce46faba76301b98fde0f1fd1a2ccc1842f9095fdbafc132
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # unreleased
2
+
3
+ - New feature: `TestData.load_rails_fixtures` to override default fixtures
4
+ behavior by loading it in a nested transaction after `TestData.truncate`
5
+ - Breaking change: move transactions configuration out of `TestData.load` and
6
+ instead a global setting for `TestData.config` named
7
+ `use_transactional_data_loader`
8
+ - Cascades truncation of test_data tables unless they're explicitly specified by
9
+ the truncate_these_test_data_tables` option
10
+ - Add secrets.yml and cable.yml generators to `test_data:configure` task
11
+ - Print the size of each dump and warn when dump size reaches certain thresholds
12
+ or increases significantly in the `test_data:dump` task
13
+
1
14
  # 0.0.2
2
15
 
3
16
  - Make the rest of the gem better
data/Gemfile.lock CHANGED
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- test_data (0.0.2)
4
+ test_data (0.1.0)
5
5
  railties (~> 6.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- actionpack (6.1.3.1)
11
- actionview (= 6.1.3.1)
12
- activesupport (= 6.1.3.1)
10
+ actionpack (6.1.4)
11
+ actionview (= 6.1.4)
12
+ activesupport (= 6.1.4)
13
13
  rack (~> 2.0, >= 2.0.9)
14
14
  rack-test (>= 0.6.3)
15
15
  rails-dom-testing (~> 2.0)
16
16
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
17
- actionview (6.1.3.1)
18
- activesupport (= 6.1.3.1)
17
+ actionview (6.1.4)
18
+ activesupport (= 6.1.4)
19
19
  builder (~> 3.1)
20
20
  erubi (~> 1.4)
21
21
  rails-dom-testing (~> 2.0)
22
22
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
23
- activesupport (6.1.3.1)
23
+ activesupport (6.1.4)
24
24
  concurrent-ruby (~> 1.0, >= 1.0.2)
25
25
  i18n (>= 1.6, < 2)
26
26
  minitest (>= 5.1)
@@ -29,18 +29,18 @@ GEM
29
29
  ast (2.4.2)
30
30
  builder (3.2.4)
31
31
  coderay (1.1.3)
32
- concurrent-ruby (1.1.8)
32
+ concurrent-ruby (1.1.9)
33
33
  crass (1.0.6)
34
34
  erubi (1.10.0)
35
35
  i18n (1.8.10)
36
36
  concurrent-ruby (~> 1.0)
37
- loofah (2.9.1)
37
+ loofah (2.10.0)
38
38
  crass (~> 1.0.2)
39
39
  nokogiri (>= 1.5.9)
40
40
  method_source (1.0.0)
41
- mini_portile2 (2.5.1)
41
+ mini_portile2 (2.5.3)
42
42
  minitest (5.14.4)
43
- nokogiri (1.11.3)
43
+ nokogiri (1.11.7)
44
44
  mini_portile2 (~> 2.5.0)
45
45
  racc (~> 1.4)
46
46
  parallel (1.20.1)
@@ -58,11 +58,11 @@ GEM
58
58
  nokogiri (>= 1.6)
59
59
  rails-html-sanitizer (1.3.0)
60
60
  loofah (~> 2.3)
61
- railties (6.1.3.1)
62
- actionpack (= 6.1.3.1)
63
- activesupport (= 6.1.3.1)
61
+ railties (6.1.4)
62
+ actionpack (= 6.1.4)
63
+ activesupport (= 6.1.4)
64
64
  method_source
65
- rake (>= 0.8.7)
65
+ rake (>= 0.13)
66
66
  thor (~> 1.0)
67
67
  rainbow (3.0.0)
68
68
  rake (13.0.3)
data/LICENSE.txt CHANGED
@@ -1,9 +1,4 @@
1
- Copyright (c) 2019 Test Double, LLC
2
-
3
- Portions of these files Copyright (c) 2012-18 Bozhidar Batsov:
4
- - config/base.yml
5
- - lib/standard/cop/block_single_line_braces.rb
6
- - test/cop_invoker.rb
1
+ Copyright (c) 2022 Test Double, LLC
7
2
 
8
3
  Permission is hereby granted, free of charge, to any person obtaining
9
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,48 +1,99 @@
1
1
  # The `test_data` gem
2
2
 
3
- `test_data` is what it says on the tin: a fast & reliable system for managing
4
- your Rails application's test data.
3
+ `test_data` does what it says on the tin: it provides a fast & reliable system
4
+ for managing your Rails application's test data.
5
5
 
6
6
  The gem serves as both an alternative to
7
7
  [fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures)
8
8
  & [factory_bot](https://github.com/thoughtbot/factory_bot), as well a broader
9
- workflow for building high-performance test suites that will scale with your
10
- application.
9
+ workflow for building test suites that will scale gracefully as your application
10
+ grows in size and complexity.
11
11
 
12
12
  What it does:
13
13
 
14
- * Establishes a fourth Rails environment named `test_data` designed to help you
15
- create a universe of data your tests can rely on by simply using your
16
- application. No Ruby DSL, no YAML files, no precarious approximations of
17
- realism: **real data created by your app**
14
+ * Establishes a fourth Rails environment (you can [define custom Rails
15
+ environments](https://guides.rubyonrails.org/configuring.html#creating-rails-environments)!)
16
+ named `test_data`, which you'll use to create a universe of data for your
17
+ tests by simply running and using your application. No Ruby DSL, no YAML
18
+ files, no precarious approximations of realism: **real data created by your
19
+ app**
18
20
 
19
21
  * Exposes a simple API for loading your test data and cleaning up between
20
- tests. Common edge cases are handled gracefully: tests that need access to
22
+ tests. Common edge cases are handled seamlessly. Tests that need access to
21
23
  your data will see it and tests that don't, won't
22
24
 
23
- * Safeguards your tests from flaky failures and supercharges their speed by
24
- providing a sophisticated transaction manager that isolates each test's
25
- changes to your data while ensuring your data is only loaded once
25
+ * Safeguards your tests from flaky failures and supercharges your build by
26
+ providing a sophisticated transaction manager that isolates each test while
27
+ ensuring your data is only loaded once
26
28
 
27
29
  If you've despaired over the seeming inevitability that all Rails test suites
28
- will eventually grow to become slow, unreliable, and brittle, then this gem is
29
- for you! And even if you're [a factory_bot
30
+ will eventually grow to become slow, incomprehensible, and brittle, then this
31
+ gem is for you! And even if you're [a factory_bot
30
32
  fan](https://twitter.com/searls/status/1379491813099253762?s=20), we hope you'll
31
33
  be open to the idea that [there might be a better way](
32
34
  #but-we-use-and-like-factory_bot-and-so-i-am-inclined-to-dislike-everything-about-this-gem).
33
35
 
34
- _[Because the gem is still brand new, it makes a number of
36
+ _[Full disclosure: because the gem is still brand new, it makes a number of
35
37
  [assumptions](#assumptions) and may not work for every project just yet.]_
36
38
 
39
+ ## Documentation
40
+
41
+ This gem requires a lot of documentation—not because `test_data` does a lot of
42
+ things, but because managing one's test data is an inherently complex task. If
43
+ one reason Rails apps chronically suffer from slow tests is that other
44
+ approaches oversimplify test data management, it stands to reason that any
45
+ discomfort caused by `test_data`'s scope may not indicate _unnecessary
46
+ complexity_ so much as highlight how much acclimatization is needed to adopt the
47
+ necessary diligence to achieve fast, isolated tests that scale with your
48
+ application.
49
+
50
+ 1. [Getting Started Guide](#getting-started-guide)
51
+ 1. [Install and initialize
52
+ `test_data`](#step-1-install-and-initialize-test_data)
53
+ 2. [Create some test data](#step-2-create-some-test-data)
54
+ 3. [Dump your `test_data` database](#step-3-dump-your-test_data-database)
55
+ 4. [Load your data in your tests](#step-4-load-your-data-in-your-tests)
56
+ 5. [Keeping your test data
57
+ up-to-date](#step-5-keeping-your-test-data-up-to-date)
58
+ 2. [Factory & Fixture Interoperability
59
+ Guide](#factory--fixture-interoperability-guide)
60
+ * [Using `test_data` with `factory_bot`](#using-test_data-with-factory_bot)
61
+ * [Using `test_data` with Rails fixtures](#using-test_data-with-rails-fixtures)
62
+ 3. [Rake Task Reference](#rake-task-reference)
63
+ * [test_data:install](#test_datainstall)
64
+ * [test_data:configure](#test_dataconfigure)
65
+ * [test_data:verify_config](#test_dataverify_config)
66
+ * [test_data:initialize](#test_datainitialize)
67
+ * [test_data:dump](#test_datadump)
68
+ * [test_data:load](#test_dataload)
69
+ * [test_data:create_database](#test_datacreate_database)
70
+ * [test_data:drop_database](#test_datadrop_database)
71
+ 4. [API Reference](#api-reference)
72
+ * [TestData.config](#testdataconfig)
73
+ * [TestData.load](#testdataload)
74
+ * [TestData.rollback](#testdatarollback)
75
+ * [TestData.rollback(:before_data_load)](#rolling-back-to-before-test-data-was-loaded)
76
+ * [TestData.rollback(:after_data_load)](#rolling-back-to-after-the-data-was-loaded)
77
+ * [TestData.rollback(:after_data_truncate)](#rolling-back-to-after-test-data-was-truncated)
78
+ * [TestData.rollback(:after_load_rails_fixtures)](#rolling-back-to-after-rails-fixtures-were-loaded)
79
+ * [TestData.truncate](#testdatatruncate)
80
+ * [TestData.prevent_rails_fixtures_from_loading_automatically!](#testdataprevent_rails_fixtures_from_loading_automatically)
81
+ * [TestData.load_rails_fixtures](#testdataload_rails_fixtures)
82
+ 5. [Assumptions](#assumptions)
83
+ 6. [Fears, Uncertainties, and Doubts](#fears-uncertainties-and-doubts) (Q & A)
84
+ 7. [Code of Conduct](#code-of-conduct)
85
+ 8. [Changelog](/CHANGELOG.md)
86
+ 9. [MIT License](/LICENSE.txt)
87
+
37
88
  ## Getting started guide
38
89
 
39
90
  This guide will walk you through setting up `test_data` in your application. You
40
91
  might notice that it's more complicated than installing a gem and declaring some
41
- default `Widget` attributes! The truth is that designing robust and reliable
42
- test data that is inherently complex and takes some thoughtful planning. There
43
- are plenty of shortcuts available, but experience has shown they tend to
44
- collapse under their own weight as your app scales and your team grows—exactly
45
- when fast & reliable tests are most valuable.
92
+ default `Widget` attributes! The hard truth is that designing robust and
93
+ reliable test data is an inherently complex problem and takes some thoughtful
94
+ planning. There are plenty of shortcuts available, but experience has shown they
95
+ tend to collapse under their own weight as your app scales and your team
96
+ grows—exactly when having a suite of fast & reliable tests is most valuable.
46
97
 
47
98
  And if you get stuck or need help as you're getting started, please feel free to
48
99
  [ask us for help](https://github.com/testdouble/test_data/discussions/new)!
@@ -62,14 +113,15 @@ end
62
113
  ```
63
114
 
64
115
  Since the `test_data` environment is designed to be used similarly to
65
- `development` (i.e. with a running server and interacting via a browser) the
66
- `:test_data` gem group should probably include everything that's available to
67
- the `:development` group.
116
+ `development` (i.e. with a running server and interacting via a browser), any
117
+ gems in your `:development` gem group should likely be included in a
118
+ `:test_data` gem group as well.
68
119
 
69
120
  #### Configuring the gem and initializing the database
70
121
 
71
- The gem ships with a number of Rake tasks, including `test_data:install`, which
72
- will generate the necessary configuration and initialize a `test_data` database:
122
+ The gem ships with a number of Rake tasks, including
123
+ [test_data:install](#test_datainstall), which will generate the necessary
124
+ configuration and initialize a `test_data` database:
73
125
 
74
126
  ```
75
127
  $ bin/rake test_data:install
@@ -118,6 +170,10 @@ environment variable:
118
170
  $ RAILS_ENV=test_data bin/rails server
119
171
  ```
120
172
 
173
+ _[If you're using [webpacker](https://github.com/rails/webpacker), you may also
174
+ need to start its development server as well with `RAILS_ENV=test_data
175
+ bin/webpack-dev-server`]_
176
+
121
177
  Because `test_data` creates a full-fledged Rails environment, you can run any
122
178
  number of Rails commands or Rake tasks against its database by setting
123
179
  `RAILS_ENV=test_data`, either in your shell environment or with each command
@@ -173,14 +229,16 @@ everything looks good—commit them. (And if the files are gigantic or full of
173
229
  noise, you might find [these ideas
174
230
  helpful](#are-you-sure-i-should-commit-these-sql-dumps-theyre-way-too-big)).
175
231
 
176
- _[Feel weird to dump and commit SQL files? That's okay! It's [healthy to be
177
- skeptical](https://twitter.com/searls/status/860553435116187649?s=20) whenever
178
- you're asked to commit a generated file! Remember that the `test_data`
232
+ Does it feel weird to dump and commit SQL files? That's okay! It's [healthy to
233
+ be skeptical](https://twitter.com/searls/status/860553435116187649?s=20)
234
+ whenever you're asked to commit a generated file! Remember that the `test_data`
179
235
  environment exists only for creating your test data. Your tests will, in turn,
180
236
  load the SQL dump of your data into the familiar `test` database, and things
181
237
  will proceed just as if you'd been loading [Rails' built-in
182
238
  fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures)
183
- from a set of YAML files—the major difference being how the data is authored.]_
239
+ from a set of YAML files—the major difference being that `test_data` databases
240
+ are generated through realistic use, whereas fixtures are defined manually in
241
+ (sometimes painstaking) YAML.
184
242
 
185
243
  ### Step 4: Load your data in your tests
186
244
 
@@ -189,9 +247,9 @@ writing tests that rely on this test data.
189
247
 
190
248
  To accomplish this, you'll likely want to add hooks to run before & after each
191
249
  test—first to load your test data and then to rollback any changes made by the
192
- test. The `test_data` gem accomplishes this with its
193
- [TestData.load](#testdataload) and [TestData.rollback](#testdatarollback)
194
- methods.
250
+ test in order to clear the air for the next test. The `test_data` gem
251
+ accomplishes this with its [TestData.load](#testdataload) and
252
+ [TestData.rollback](#testdatarollback) methods.
195
253
 
196
254
  If you're using (Rails' default)
197
255
  [Minitest](https://github.com/seattlerb/minitest) and want to include your test
@@ -199,11 +257,11 @@ data with every test, you can add these hooks to `ActiveSupport::TestCase`:
199
257
 
200
258
  ```ruby
201
259
  class ActiveSupport::TestCase
202
- def setup
260
+ setup do
203
261
  TestData.load
204
262
  end
205
263
 
206
- def teardown
264
+ teardown do
207
265
  TestData.rollback
208
266
  end
209
267
  end
@@ -224,47 +282,52 @@ RSpec.configure do |config|
224
282
  end
225
283
  ```
226
284
 
227
- That should be all you need to have access to your test data in all of your
228
- tests, along with the speed and data integrity of wrapping each test in an
229
- always-rolled-back transaction. For more information on how all this works, see
230
- the [API reference](#api-reference).
285
+ That should be all you need to have access to your test data in each of your
286
+ tests! Your tests will also benefit from the speed and data integrity that comes
287
+ with wrapping each test in an always-rolled-back transaction. For more
288
+ information on how all this works, see the [API reference](#api-reference). If
289
+ your test suite is already using fixtures or factories and the above hooks just
290
+ broke everything, check out our [interoperability
291
+ guide](#factory--fixture-interoperability-guide) for help.
231
292
 
232
293
  If you _don't_ want all of your Rails-aware tests to see this test data (suppose
233
- you have existing tests that use factories or fixtures instead), you probably
294
+ you have existing tests that use factories instead), you probably
234
295
  want to use [TestData.truncate](#testdatatruncate) to clear data generated by
235
296
  this gem out before they run. You might do that by defining two test types:
236
297
 
237
298
  ```ruby
238
299
  # Tests using data created by `test_data`
239
300
  class TestDataTestCase < ActiveSupport::TestCase
240
- def setup
301
+ setup do
241
302
  TestData.load
242
303
  end
243
304
 
244
- def teardown
305
+ teardown do
245
306
  TestData.rollback
246
307
  end
247
308
  end
248
309
 
249
- # Tests using data created by `factory_bot`
250
- class FactoryBotTestCase < ActiveSupport::TestCase
251
- include FactoryBot::Syntax::Methods
252
-
253
- def setup
310
+ # Tests that don't need a shared data source
311
+ class SomeModelTestCase < ActiveSupport::TestCase
312
+ setup do
254
313
  TestData.truncate
255
314
  end
256
315
 
257
- def teardown
316
+ def test_some_model_does_stuff
317
+ some_model = SomeModel.create!
318
+
319
+ result = some_model.does_stuff
320
+
321
+ assert_equal :cool_stuff, result
322
+ end
323
+
324
+ teardown do
258
325
  TestData.rollback(:after_data_truncate)
259
326
  end
260
327
  end
261
328
 
262
329
  ```
263
330
 
264
- For more thoughts on migrating to `test_data` when you have existing tests,
265
- [some ideas are discussed
266
- here](#we-already-have-thousands-of-tests-that-depend-on-rails-fixtures-or-factory_bot-can-we-start-using-test_data-without-throwing-them-away-and-starting-over).
267
-
268
331
  ### Step 5: Keeping your test data up-to-date
269
332
 
270
333
  Because your app relies on its tests and your tests rely on their test data,
@@ -282,17 +345,18 @@ data. Here's a rough outline to updating your `test_data` database:
282
345
  1. If your local `test_data` database is out-of-date with your latest SQL dump
283
346
  files, drop it with `rake test_data:drop_database`
284
347
 
285
- 2. Load your schema & data into the database with `rake test_data:load`
348
+ 2. Load your schema & data into the `test_data` database with `rake
349
+ test_data:load`
286
350
 
287
351
  3. Run any pending migrations with `RAILS_ENV=test_data bin/rake db:migrate`
288
352
 
289
353
  4. If you need to create any additional data, start up the server
290
- (`RAILS_ENV=test_data bin/rails s`), just like [Step
354
+ (`RAILS_ENV=test_data bin/rails s`), just like in [Step
291
355
  2](#step-2-create-some-test-data)
292
356
 
293
357
  5. Export your newly-updated `test_data` database with `rake test_data:dump`
294
358
 
295
- 6. Ensure your tests are passing and commit the resulting SQL files
359
+ 6. Ensure your tests are passing and then commit the resulting SQL files
296
360
 
297
361
  It's important to keep in mind that your test data SQL dumps are a shared,
298
362
  generated resource among your team (just like a `structure.sql` or `schema.rb`
@@ -309,6 +373,361 @@ time of things if you use migrations for both schema and data changes. Here are
309
373
  some notes on [how to write data migrations
310
374
  safely](https://blog.testdouble.com/posts/2014-11-04-healthy-migration-habits/#habit-4-dont-reference-models).]_
311
375
 
376
+ ## Factory & Fixture Interoperability Guide
377
+
378
+ Let's be real, most Rails apps already have some tests, and most of those test
379
+ suites will already be relying on
380
+ [factory_bot](https://github.com/thoughtbot/factory_bot) or Rails' built-in
381
+ [test
382
+ fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures).
383
+ While `test_data` is designed to be an alternative to both of these approaches
384
+ to managing your test data, it wouldn't be practical to ask a team to rewrite
385
+ all their existing tests in order to migrate to a different tool. That's why the
386
+ `test_data` gem goes to great lengths to play nicely with your existing tests,
387
+ while ensuring each test is wrapped in an isolated and fast always-rolled-back
388
+ transaction—regardless if the test depends on `test_data`, factories, fixtures,
389
+ all three, or none-of-the-above.
390
+
391
+ This section will hopefully make it a little easier to incorporate new
392
+ `test_data` tests into a codebase that's already using `factory_bot` and/or
393
+ Rails fixtures, whether you choose to incrementally rewrite the older tests to
394
+ conform to your `test_data` or not.
395
+
396
+ ### Using `test_data` with `factory_bot`
397
+
398
+ This section will document some thoughts and strategies for introducing
399
+ `test_data` to a test suite that's already using `factory_bot`.
400
+
401
+ #### Getting your factory tests passing after adding `test_data`
402
+
403
+ Depending on the assumptions your tests make about the state of the database
404
+ before you've loaded any factories, it's possible that everything will "just
405
+ work" after adding `TestData.load` in a before-each hook and `TestData.rollback`
406
+ in an after-each hook (as shown in the [setup
407
+ guide](#step-4-load-your-data-in-your-tests)). So by all means, try running your
408
+ suite after following the initial setup guide and see if the suite just passes.
409
+
410
+ If you find that your test suite is failing after adding `TestData.load` to your
411
+ setup, don't panic! It probably means that you have test data and factory
412
+ invocations that are, when combined, violating unique validations or database
413
+ constraints. Depending on your situation (e.g. the size of your test suite, how
414
+ well you understand the intentions of older tests) you might try to resolve
415
+ these errors one-by-one, usually by updating the offending factories or
416
+ editing your `test_data` database to ensure they steer clear of one another.
417
+ This can be tedious, however, and can undermine the completeness of either data
418
+ source in the absence of the other.
419
+
420
+ If your tests are failing after introducing `test_data` and it's not desirable
421
+ or feasible to work through the individual failures, you can accomplish a clean
422
+ segregation between your factory-dependent tests and your tests that rely on
423
+ `test_data` by wrapping each test that depends on `factory_bot` with
424
+ [TestData.truncate](#testdatatruncate) in a before-each hook and
425
+ [TestData.rollback(:after_data_truncate)](#rolling-back-to-after-test-data-was-truncated)
426
+ in an after-each hook, like this:
427
+
428
+ ```ruby
429
+ class AnExistingFactoryUsingTest < ActiveSupport::Testcase
430
+ def setup
431
+ TestData.truncate
432
+ # pre-existing setup
433
+ end
434
+
435
+ def test_stuff
436
+ #… etc
437
+ end
438
+
439
+ def teardown
440
+ TestData.rollback(:after_truncate)
441
+ end
442
+ end
443
+ ```
444
+
445
+ What this will do is complicated and counter-intuitive, but also fast and
446
+ reliable: [TestData.truncate](#testdatatruncate) will first ensure that your
447
+ `test_data` database is loaded inside a transaction, then will truncate that
448
+ data (set the `truncate_these_test_data_tables` [config option](#testdataconfig)
449
+ if necessary), and will finally create _yet another_ transaction save point
450
+ named `:after_data_truncate`. From that point onward, your test is free to
451
+ create all the factories it needs without fear of colliding with whatever you've
452
+ got stored in your `test_data` tables.
453
+
454
+ _[Why does this approach potentially load all the `test_data` data only to
455
+ immediately truncate it? Because it's actually much faster to truncate a large
456
+ data load in a live transaction, rollback the truncation, and then re-truncate
457
+ the data for a subsequent test than it would be to rollback the large data load
458
+ itself and re-load it for a subsequent test. It's silly but it works.]_
459
+
460
+ Hopefully one of these approaches, or some combination of them will get your
461
+ test suite passing after you've introduced `test_data`.
462
+
463
+ #### Separating your `test_data` and factory tests
464
+
465
+ Just because your tests _can_ access both your `factory_bot` factories and
466
+ `test_data` database doesn't mean they _should_.
467
+
468
+ Integration tests inevitably become coupled to the data that's available to
469
+ them, and if a test has access to both records created by a factory and a
470
+ `test_data` SQL dump, it is likely to unintentionally become inextricable from
471
+ both. This could result in the test having more ways to fail than necessary and
472
+ make it harder to simplify your test data strategy later. Instead, consider
473
+ explicitly opting into a single type of test data by separating your tests based
474
+ on which source of test data they use.
475
+
476
+ Every situation will be different, but one strategy that suits a lot of
477
+ circumstances would be to write a class method that runs at test-load time to
478
+ declare and configure the test data strategy for the current test.
479
+
480
+ Taking from [this
481
+ example](/example/test/integration/better_mode_switching_demo_test.rb) test, you
482
+ could implement a class method like this:
483
+
484
+ ```ruby
485
+ class ActiveSupport::TestCase
486
+ def self.test_data_mode(mode)
487
+ case mode
488
+ when :factory_bot
489
+ require "factory_bot_rails"
490
+ include FactoryBot::Syntax::Methods
491
+
492
+ setup do
493
+ TestData.truncate
494
+ end
495
+
496
+ teardown do
497
+ TestData.rollback(:after_data_truncate)
498
+ end
499
+ when :test_data
500
+ setup do
501
+ TestData.load
502
+ end
503
+
504
+ teardown do
505
+ TestData.rollback
506
+ end
507
+ end
508
+ end
509
+ end
510
+ ```
511
+
512
+ And then (without any class inheritance complications), simply declare which
513
+ kind of test you're specifying:
514
+
515
+ ```ruby
516
+ class SomeFactoryUsingTest < ActiveSupport::TestCase
517
+ test_data_mode :factory_bot
518
+
519
+ # … tests go here
520
+ end
521
+
522
+ class SomeTestDataUsingTest < ActionDispatch::IntegrationTest
523
+ test_data_mode :test_data
524
+
525
+ # etc.
526
+ end
527
+ ```
528
+
529
+ By following an approach like this one, your `test_data` tests won't even see
530
+ your `create_*` factory methods and your `factory_bot` tests won't have access
531
+ to any of your `test_data`, either. From there, you can migrate tests onto
532
+ `test_data` incrementally, secure in the knowledge that you're not inadvertently
533
+ tangling your tests' dependency graph further.
534
+
535
+ #### Speeding up your test suite when using factories
536
+
537
+ ##### Addressing redundant data cleanup
538
+
539
+ After adding `test_data` to your test suite, consider is how database cleanup
540
+ was being handled previously to make sure it isn't unnecessarily truncating
541
+ everything or resetting the transaction between tests. It's possible that your
542
+ suite is relying on Rails' built-in `use_transactional_tests` feature to wrap
543
+ your tests in always-rolled-back transactions, even if you're not using
544
+ fixtures. Or perhaps your suite uses
545
+ [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) to
546
+ truncate the database before or after each test. In either case, it's important
547
+ to know that by default [TestData.load](#testdataload) and
548
+ [TestData.rollback](#testdatarollback) will start and rollback a nested
549
+ transaction, respectively. That means—so long as they're called at the top of a
550
+ before-each hook and the end of an after-each hook—you might be able to disable
551
+ `use_transactional_tests` or remove your dependency on `database_cleaner` or any
552
+ other custom truncation logic you might have. Even if you get your suite running
553
+ immediately after adding `test_data`, it's still worth taking the time to
554
+ understand what's going on during test setup & teardown, because there may be an
555
+ opportunity to make your tests faster and more comprehensible by eliminating
556
+ redundant clean-up steps.
557
+
558
+ ##### Avoiding truncate rollback churn
559
+
560
+ It's important to know that if your test suite has a mix of tests that call
561
+ [TestData.load](#testdataload) and tests that call
562
+ [TestData.truncate](#testdatatruncate), each time the test runner switches
563
+ between the two types, each call to `TestData.load` will cause the transaction
564
+ state to be rolled back from
565
+ [:after_data_truncate](#rolling-back-to-after-test-data-was-truncated) to
566
+ [:after_data_load](#rolling-back-to-after-the-data-was-loaded), only for the
567
+ next test to call `TestData.truncate` truncates all the tables again. In
568
+ practice, this shouldn't be too costly an operation, but if your test order is
569
+ randomized you might find that your build will run faster if you separate each
570
+ set of tests at runtime.
571
+
572
+ Separating your `test_data` and `factory_bot` tests is pretty trivial if you're
573
+ using RSpec, as the
574
+ [tag](https://relishapp.com/rspec/rspec-core/v/3-10/docs/command-line/tag-option)
575
+ feature was built with this sort of need in mind. Otherwise, you might consider
576
+ organizing the tests in different directories and running multiple commands to
577
+ execute them (e.g. `bin/rails test test/test_data_tests` and `bin/rails
578
+ test/factory_tests`). Every CI configuration is different, however, and you may
579
+ find yourself needing to get creative in configuring things to achieve the
580
+ fastest build time.
581
+
582
+ ### Using `test_data` with Rails fixtures
583
+
584
+ While [Rails
585
+ fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures)
586
+ are similar to factories, the fact that they're run globally by Rails and
587
+ permanently committed to the test database actually makes them a little trickier
588
+ to work with. This section will cover a couple approaches for integrating
589
+ `test_data` into suites that use fixtures.
590
+
591
+ #### Getting your fixtures-dependent tests passing with `test_data`
592
+
593
+ It's more likely than not that all your tests will explode in dramatic fashion
594
+ as soon as you add `TestData.load` to a `setup` or `before(:each)` hook. Because
595
+ fixtures will be loaded all-at-once then your `test_data` dump will be inserted
596
+ directly on top of them. If everything works, or if you only encounter a few
597
+ errors throughout your test suite (perhaps based on assertions of the `count` of
598
+ a particular model), congratulations! You should still consider mitigating the
599
+ risks of coupling your tests to both data sources ([as discussed
600
+ above](#separating-your-test_data-and-factory-tests)) by migrating completely
601
+ onto `test_data` over time, but no further action is necessary or recommended.
602
+
603
+ If, however, you find yourself running into non-trivial challenges (like rampant
604
+ validation or constraint errors), `test_data` provides an API that **overrides
605
+ Rails' built-in fixtures behavior with a monkey patch**. If that bold text
606
+ warning wasn't enough to scare you from reading on, here's how to do it.
607
+
608
+ _[Note that the following requires `use_transactional_data_loader` to be enabled
609
+ in your [config](#testdataconfig), because it depends on transaction
610
+ rollbacks.]_
611
+
612
+ Here's what you can do if you can't get your fixtures to play nicely with your
613
+ `test_data` dump:
614
+
615
+ 1. Near the top of your test helper, call:
616
+ [TestData.prevent_rails_fixtures_from_loading_automatically!](#testdataprevent_rails_fixtures_from_loading_automatically)
617
+ This will effectively turn
618
+ [setup_fixtures](https://github.com/rails/rails/blob/main/activerecord/lib/active_record/test_fixtures.rb#L105)
619
+ into a no-op, which means that your test fixtures will not be automatically
620
+ loaded into your test database
621
+
622
+ 2. In tests that rely on your `test_data` dump, call [TestData.load and
623
+ TestData.rollback](#step-4-load-your-data-in-your-tests) as you normally
624
+ would. Because your fixtures won't be loaded automatically, they won't be
625
+ available to these tests
626
+
627
+ 3. In tests that need fixtures, call
628
+ [TestData.load_rails_fixtures](#testdataload_rails_fixtures)
629
+ in a before-each hook and
630
+ [TestData.rollback(:after_load_rails_fixtures)](#rolling-back-to-after-rails-fixtures-were-loaded)
631
+ in an after-each hook. This will (in an almost comic level of
632
+ transaction-nesting) ensure your `test_data` dump is loaded in an initial
633
+ transaction, then ensure that it is truncated in a second transaction, before
634
+ loading your rails fixtures in a third transaction. These tests will have
635
+ access to all your fixture data without being tainted by any of your
636
+ `test_data` data
637
+
638
+ For example, you might add the following to an existing fixtures-dependent
639
+ test to get it passing:
640
+
641
+ ```ruby
642
+ class AnExistingFixtureUsingTest < ActiveSupport::Testcase
643
+ def setup
644
+ TestData.load_rails_fixtures
645
+ # pre-existing setup
646
+ end
647
+
648
+ def test_stuff
649
+ #… etc
650
+ end
651
+
652
+ def teardown
653
+ TestData.rollback(:after_load_rails_fixtures)
654
+ end
655
+ end
656
+ ```
657
+
658
+ _[You don't need to worry about whether `TestData.load` has been called
659
+ previously, the loader will infer your intent and ensure that the transaction
660
+ state is correct before loading your fixtures.]_
661
+
662
+ #### Separating your `test_data` and fixture tests
663
+
664
+ *This only applies if you had to use
665
+ [TestData.load_rails_fixtures](#testdataload_rails_fixtures) as shown above.*
666
+
667
+ Just [like with factories](#separating-your-test_data-and-factory-tests), you
668
+ might benefit from a test helper to clearly declare whether a test uses fixtures
669
+ or `test_data` right at the top. Following the same pattern, you might do this:
670
+
671
+ ```ruby
672
+ class ActiveSupport::TestCase
673
+ def self.test_data_mode(mode)
674
+ case mode
675
+ when :fixtures
676
+ fixtures :all
677
+
678
+ setup do
679
+ TestData.load_rails_fixtures
680
+ end
681
+
682
+ teardown do
683
+ TestData.rollback(:after_load_rails_fixtures)
684
+ end
685
+ when :test_data
686
+ setup do
687
+ TestData.load
688
+ end
689
+
690
+ teardown do
691
+ TestData.rollback
692
+ end
693
+ end
694
+ end
695
+ end
696
+ ```
697
+
698
+ Which would allow you to simplify the above fixtures-using test to:
699
+
700
+ ```ruby
701
+ class AnExistingFixtureUsingTest < ActiveSupport::Testcase
702
+ test_data_mode :fixtures
703
+
704
+ def test_stuff
705
+ #… etc
706
+ end
707
+ end
708
+ ```
709
+
710
+ #### Improving test suite speed with fixtures
711
+
712
+ Again, as is [the case with
713
+ factories](#improving-test-suite-speed-with-factories), every time your test
714
+ runner randomly picks a `test_data` test after running a fixtures-dependent
715
+ test, it will roll back your fixtures and the truncation of your `test_data`,
716
+ only to re-truncate your `test_data` data and reload your fixtures for the next
717
+ test that happens to use fixtures. But unlike truncation alone, loading your
718
+ fixtures is a non-trivial operation that can chew up a some serious time as your
719
+ suite runs.
720
+
721
+ As a result, we strongly encourage breaking up your test suite to avoid this
722
+ churn, even if it means splitting your test run over multiple CLI commands. If
723
+ you're using the Rails test runner and Minitest, that likely means sequestering
724
+ one set of tests to one directory and the other to a different directory, as
725
+ there is no granular control over to how the runner randomizes suites. And for
726
+ RSpec,
727
+ [tagging](https://relishapp.com/rspec/rspec-core/v/3-10/docs/command-line/tag-option)
728
+ each spec and running separate commands for each tag could yield significant
729
+ performance improvements.
730
+
312
731
  ## Rake Task Reference
313
732
 
314
733
  ### test_data:install
@@ -326,23 +745,43 @@ This task runs several generators:
326
745
  too! This gem adds a new `test_data` environment and database that's intended
327
746
  to be used to create and dump your test data. This new environment file loads
328
747
  your `development` environment's configuration and disables migration schema
329
- dumps so that you can run migrations of your `test_data` database without
748
+ dumps so that you can run migrations against your `test_data` database without
330
749
  affecting your app's `schema.rb` or `structure.sql`.
331
750
 
332
- * `config/initializers/test_data.rb` - Calls [TestData.config](#testdataconfig)
333
- with an empty block and comments documenting the currently-available options
334
- and their default values
751
+ * `config/initializers/test_data.rb` - Creates an initializer for the gem that
752
+ calls [TestData.config](#testdataconfig) with an empty block and comments
753
+ documenting the currently-available options and their default values
335
754
 
336
755
  * `config/database.yml` - This generator adds a new `test_data` section to your
337
756
  database configuration, named with the same scheme as your other databases
338
757
  (e.g. `your_app_test_data`). If your configuration resembles Rails' generated
339
- database.yml and has a working `&default` alias, then this should "just work"
340
-
341
- * `config/webpacker.yml` - The gem has nothing to do with web assets or
342
- webpacker, but webpacker will display some prominent warnings or errors if it
343
- is loaded without a configuration entry for the currently-running environment,
344
- so this generator defines an alias based on your `development` config and then
345
- defines `test_data` as extending it
758
+ `database.yml` and has a working `&default` alias, then this should "just
759
+ work"
760
+
761
+ * `config/webpacker.yml` - The gem has nothing to do with web assets, but
762
+ [webpacker](https://github.com/rails/webpacker) will display some prominent
763
+ warnings or errors if it is loaded without a configuration entry for the
764
+ currently-running environment, so this generator defines an alias based on
765
+ your `development` config and then defines `test_data` as extending it
766
+
767
+ * `config/secrets.yml` - If your app still uses (the now-deprecated)
768
+ [secrets.yml](https://guides.rubyonrails.org/4_1_release_notes.html#config-secrets-yml)
769
+ file introduced in Rails 4.1, this generator will ensure that the `test_data`
770
+ environment is accounted for with a generated `secret_key_base` value. If you
771
+ have numerous secrets in this file's `development:` stanza, you may want to
772
+ alias and inherit it into `test_data:` like the `webpacker.yml` generator does
773
+
774
+ * `config/cable.yml` - In the absences of a configuration stanza,
775
+ [ActionCable](https://guides.rubyonrails.org/action_cable_overview.html) will
776
+ assume you're using Redis for tracking Websocket connections, so this
777
+ generator explicitly specifies `async` instead, since that's the default for
778
+ `development:`
779
+
780
+ ### test_data:verify_config
781
+
782
+ This task will verify that your configuration appears to be valid by checking
783
+ with each of the gem's generators to inspect your configuration files, and will
784
+ error whenever a configuration problem is detected.
346
785
 
347
786
  ### test_data:initialize
348
787
 
@@ -353,22 +792,23 @@ your seed file. Specifically:
353
792
  1. Creates the `test_data` environment's database, if it doesn't already exist
354
793
 
355
794
  2. Ensures the database is non-empty to preserve data integrity (run
356
- `test_data:drop_database` first if it contains outdated test data)
795
+ [test_data:drop_database](#test_datadrop_database) first if it contains
796
+ outdated test data)
357
797
 
358
798
  3. Checks to see if a dump of the database already exists (by default, stored in
359
799
  `test/support/test_data/`)
360
800
 
361
- * If dumps do exist, it invokes `test_data:load` to load them into the
362
- database
801
+ * If dumps do exist, it invokes [test_data:load](#test_dataload) to load
802
+ them into the database
363
803
 
364
804
  * Otherwise, it invokes the task `db:schema:load` and `db:seed` (similar to
365
- the `db:setup` task)
805
+ Rails' built-in `db:setup` task)
366
806
 
367
807
  ### test_data:dump
368
808
 
369
809
  This task is designed to be run after you've created or updated your test data
370
- and you want to run tests against it. The task creates several plain SQL dumps
371
- from your `test_data` environment's database:
810
+ and you're ready to run your tests against it. The task creates several plain
811
+ SQL dumps from your `test_data` environment's database:
372
812
 
373
813
  * A schema-only dump, by default in `test/support/test_data/schema.sql`
374
814
 
@@ -389,7 +829,7 @@ options to control which tables are exported into which group.
389
829
  This task will load your SQL dumps into your `test_data` database by:
390
830
 
391
831
  1. Verifying the `test_data` environment's database is empty (creating it if it
392
- doesn't exist)
832
+ doesn't exist and failing if it's not empty)
393
833
 
394
834
  2. Verifying that your schema, test data, and non-test data SQL dumps can be
395
835
  found at the configured paths
@@ -409,8 +849,10 @@ $ bin/rake test_data:dump
409
849
  ### test_data:create_database
410
850
 
411
851
  This task will create the `test_data` environment's database if it does not
412
- already exist. It also enhances Rails' `db:create` task so that `test_data` is
413
- created along with `development` and `test`.
852
+ already exist. It also
853
+ [enhances](https://dev.to/molly/rake-task-enhance-method-explained-3bo0) Rails'
854
+ `db:create` task so that `test_data` is created along with `development` and
855
+ `test`.
414
856
 
415
857
  ### test_data:drop_database
416
858
 
@@ -449,6 +891,12 @@ TestData.config do |config|
449
891
  # `data_dump_path` will be truncated
450
892
  # config.truncate_these_test_data_tables = nil
451
893
 
894
+ # Perform TestData.load and TestData.truncate inside nested
895
+ # transactions for increased test isolation and speed. Setting this
896
+ # to false will disable several features that depend on transactions
897
+ # being used
898
+ # config.use_transactional_data_loader = true
899
+
452
900
  # Log level (valid values: [:debug, :info, :warn, :error, :quiet])
453
901
  # Can also be set with env var TEST_DATA_LOG_LEVEL
454
902
  # config.log_level = :info
@@ -468,7 +916,7 @@ savepoints](https://www.postgresql.org/docs/current/sql-savepoint.html)). By
468
916
  default, data is loaded in a transaction and intended to be rolled back to the
469
917
  point _immediately after_ the data was imported after each test. This way, your
470
918
  test suite only pays the cost of importing the SQL file once, but each of your
471
- tests can enjoy a clean slate free of potential data pollution from prior tests.
919
+ tests can enjoy a clean slate that's free of data pollution from other tests.
472
920
  (This is similar to, but separate from, Rails fixtures'
473
921
  [use_transactional_tests](https://edgeguides.rubyonrails.org/testing.html#testing-parallel-transactions)
474
922
  option.)
@@ -477,22 +925,24 @@ To help think through the method's behavior, the method nicknames its
477
925
  transactions `:before_data_load` and `:after_data_load`. The first time you call
478
926
  `TestData.load`:
479
927
 
480
- 1. Starts the `:before_data_load` transaction
928
+ 1. Creates the `:before_data_load` savepoint
481
929
  2. Executes the SQL found in the data dump (e.g.
482
930
  `test/support/test_data/data.sql`) to insert your test data
483
- 3. Starts the `:after_data_load` transaction
931
+ 3. Creates the `:after_data_load` savepoint
484
932
 
485
933
  If the method is called and the `:after_data_load` savepoint is already active
486
934
  (indicating that the data is loaded), the method rolls back to
487
935
  `:after_data_load`, inferring that the user's intention is to have a clean load
488
936
  of the test data.
489
937
 
490
- _[As an additional safeguard, in case a rollback is triggered unexpectedly (i.e.
938
+ As an additional safeguard, in case a rollback is triggered unexpectedly (i.e.
491
939
  calling `rollback_transaction` on `ActiveRecord::Base.connection` instead of via
492
- `TestData.rollback`), `test_data` writes a memo that the data is loaded in
493
- `ar_internal_metadata`. `TestData.load` uses this memo to detect this issue and
494
- will recreate the `:after_data_load` savepoint rather than attempt to
495
- erroneously reload your SQL data dump.]_
940
+ `TestData.rollback`), `test_data` writes a memo indicating that the data is
941
+ loaded in `ar_internal_metadata`. `TestData.load` uses this memo to detect this
942
+ issue and will recreate the `:after_data_load` savepoint rather than attempt to
943
+ erroneously reload your SQL data dump. (Similar error-handling is built-into
944
+ [TestData.truncate](#testdatatruncate) and
945
+ [TestData.load_rails_fixtures](#testdataload_rails_fixtures), as well.)
496
946
 
497
947
  #### Loading without transactions
498
948
 
@@ -501,15 +951,15 @@ strategy, both because it's faster and because it reduces the risk of test
501
951
  pollution. However, you may need to commit your test data if the data needs to
502
952
  be loaded by multiple processes or over multiple connections.
503
953
 
504
- If you need to load the test data and commit it to the database, simply call
505
- `TestData.load(transactions: false)`.
954
+ If you need to load the test data and commit it to the database, simply set
955
+ `TestData.config.use_transactional_data_loader = false`.
506
956
 
507
- Once committed, figuring out when and how to clear it out after each test run
508
- then becomes an additional responsibility. Many folks use
957
+ If transactions are disabled, you'll need to decide whether and how to clear the
958
+ data out after each test. Many folks use
509
959
  [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) for
510
- this, while this gem offers a rudimentary
960
+ this, while `test_data` offers a rudimentary
511
961
  [TestData.truncate](https://github.com/testdouble/test_data#testdatatruncate)
512
- method that can be passed `transactions: false` and which may be sufficient.
962
+ method that may be sufficient for your needs.
513
963
 
514
964
  You might imagine something like this if you were loading the data just once for
515
965
  the full run of a test suite:
@@ -517,38 +967,39 @@ the full run of a test suite:
517
967
  ```ruby
518
968
  RSpec.configure do |config|
519
969
  config.before :all do
520
- TestData.load(transactions: false)
970
+ TestData.load
521
971
  end
522
972
 
523
973
  config.after :all do
524
- TestData.truncate(transactions: false)
974
+ TestData.truncate
525
975
  end
526
976
  end
527
977
  ```
528
978
 
529
- Note that subsequent `TestData.load(transactions: false)` calls won't be able to
530
- detect whether the data is already loaded and will try to re-insert the data,
531
- which will almost certainly result in primary key conflicts.
979
+ Note that when `use_transactional_data_loader` is `false`, subsequent
980
+ `TestData.load` calls won't be able to detect whether the data is already loaded
981
+ and will try to re-insert the data, which will almost certainly result in
982
+ primary key conflicts.
532
983
 
533
984
  ### TestData.rollback
534
985
 
535
986
  Because the gem loads your data in a transaction, it makes it easy to rollback
536
- to any of its defined savepoints (`:before_data_load`, `:after_data_load`, and
537
- `:after_data_truncate`). In most cases you'll want to roll back to
987
+ to any of its defined savepoints. In most cases you'll want to roll back to
538
988
  `:after_data_load` after each test, and that's what `TestData.rollback` will do
539
- when called without an argument. More details on rolling back to each of the
540
- gem's savepoints follows below.
989
+ when called without an argument. If the specified savepoint isn't active,
990
+ calling `rollback` is a no-op.
541
991
 
542
- #### Rolling back to after the data was loaded
992
+ The gem may create up to four nested savepoints in a single transaction, and
993
+ this method allows you to rollback to any of them. They form the following
994
+ stack:
543
995
 
544
- When `TestData.rollback` is passed no arguments or called more explicitly as
545
- `TestData.rollback(:after_data_load)`, the method will rollback to the
546
- `:after_data_load` transaction savepoint taken immediately after the SQL dump
547
- was loaded. As a result, it is intended to be run after each test (e.g. in an
548
- `after(:each)` or `teardown`), to undo any changes made by the test.
996
+ * `:before_data_load` - Taken before loading your `test_data` dump
997
+ * `:after_data_load` - Taken after loading your `test_data` dump
998
+ * `:after_truncate` - Taken after your `test_data` is truncated
999
+ * `:after_load_rails_fixtures` - Taken after Rails fixtures are loaded via
1000
+ [TestData.load_rails_fixtures](#testdataload_rails_fixtures)
549
1001
 
550
- (Calling `TestData.rollback` when no `:after_data_load` save point is active is
551
- a no-op.)
1002
+ More details on rolling back to each of the gem's savepoints follows below.
552
1003
 
553
1004
  #### Rolling back to before test data was loaded
554
1005
 
@@ -558,13 +1009,10 @@ that depend on that data _not being there_, you probably want to call
558
1009
  you can rewind to the moment just before your test data was loaded by calling
559
1010
  `TestData.rollback(:before_data_load)`.
560
1011
 
561
- (Calling `TestData.rollback` when no `:before_data_load` save point is active is
562
- a no-op.)
563
-
564
- **⚠️ Warning:** Repeatedly loading and rolling back to `:before_data_load` is
1012
+ **⚠️ Warning⚠️** Repeatedly loading and rolling back to `:before_data_load` is
565
1013
  expensive! If your test suite calls `TestData.rollback(:before_data_load)`
566
1014
  multiple times, it's likely you're re-loading your (possibly large) SQL file of
567
- test data more times than is necessary. Consider using
1015
+ test data many more times than is necessary. Consider using
568
1016
  [TestData.truncate](#testdatatruncate) to achieve the same goal with faster
569
1017
  performance. Failing that, it might be preferable to partition your test suite
570
1018
  so that similar tests are run in separate groups (as opposed to in a fully
@@ -572,27 +1020,41 @@ random or arbitrary order) to avoid repeatedly thrashing between rollbacks and
572
1020
  reloads. This partitioning could be accomplished by either configuring your test
573
1021
  runner or by running separate test commands for each group of tests.
574
1022
 
1023
+ #### Rolling back to after the data was loaded
1024
+
1025
+ This is the way you're likely to call this method most often.
1026
+
1027
+ When `TestData.rollback` is passed no arguments or called more explicitly as
1028
+ `TestData.rollback(:after_data_load)`, the method will rollback to the
1029
+ `:after_data_load` transaction savepoint taken immediately after the SQL dump
1030
+ was loaded. As a result, it is intended to be run after each test (e.g. in an
1031
+ `after(:each)` or `teardown`), to undo any changes made by the test.
1032
+
575
1033
  #### Rolling back to after test data was truncated
576
1034
 
577
1035
  If some of your tests call [TestData.truncate](#testdatatruncate) to clear out
578
- your test data after it's been loaded, then you will likely want to run
579
- `TestData.rollback(:after_data_truncate)` after each of them. This will rewind
580
- your test database's state to when those tables were first truncated—effectively
581
- re-cleaning the slate for the next test.
1036
+ your test data after it's been loaded (as
1037
+ [described](#getting-your-factory-tests-passing-after-adding-test_data) when
1038
+ using `test_data` in conjunction with `factory_bot`), then you will likely want
1039
+ to run `TestData.rollback(:after_data_truncate)` after each of them. This will
1040
+ rewind your test database's state to when those tables were first
1041
+ truncated—effectively re-cleaning the slate for the next test.
1042
+
1043
+ #### Rolling back to after Rails fixtures were loaded
582
1044
 
583
- (Calling `TestData.rollback` when no `:after_data_truncate` save point is active
584
- is a no-op.)
1045
+ If you're using [TestData.load_rails_fixtures](#testdataload_rails_fixtures) in
1046
+ your test's before-each hook, you'll probably want to teardown that test by
1047
+ rolling back with `TestData.rollback(:after_load_rails_fixtures)` in an
1048
+ after-each hook, which will rewind to the point just after your Rails fixtures
1049
+ were loaded.
585
1050
 
586
1051
  ### TestData.truncate
587
1052
 
588
1053
  Do you have some tests that _shouldn't_ access your test data? Or did some
589
- existing tests started failing after `test_data` was added? If you want to reset
590
- the state of your `test` database to support these tests, you have two options:
591
- (1) `TestData.rollback(:before_data_load)` and (2) `TestData.truncate`. As
592
- discussed above, the former will do the job, but may necessitate repetitive,
593
- slow reloads of your test data SQL file. `TestData.truncate`, meanwhile,
594
- truncates all the tables that `TestData.load` inserted into and then creates
595
- a savepoint nicknamed `:after_data_truncate`.
1054
+ existing tests started failing after `test_data` was added? If you want to clear
1055
+ the state of your `test` database to support these tests, you can accomplish
1056
+ this with `TestData.truncate`. It truncates all the tables that `TestData.load`
1057
+ inserted into and then creates a savepoint named `:after_data_truncate`.
596
1058
 
597
1059
  Most often, you'll want to call `TestData.truncate` before each test that
598
1060
  should _not_ have access to your test data created with this gem. After each
@@ -611,28 +1073,54 @@ end
611
1073
  ```
612
1074
 
613
1075
  By default, all tables for which there is an `INSERT INTO` statement in your
614
- test data SQL dump will be truncated, but you can specify which tables should be
615
- truncated yourself by setting the `truncate_these_test_data_tables` property on
616
- [TestData.config](#testdataconfig) to an array of table names.
1076
+ test data SQL dump will be truncated (and cascading to any tables with foreign
1077
+ keys pointing to those tables), but you can also explicitly specify which tables
1078
+ should be truncated yourself by setting the `truncate_these_test_data_tables`
1079
+ property on [TestData.config](#testdataconfig) to an array of table names.
617
1080
 
618
- Because tests are usually run in a random order, some fault tolerance is built
619
- in:
1081
+ #### If you're not using transactions
620
1082
 
621
- * If one test calls `TestData.rollback(:after_data_truncate)` in `teardown`, and
622
- the next test calls `TestData.load` in `setup`, the gem will do the
623
- probably-intended thing and rollback to `:after_data_load` so that the data is
624
- available
1083
+ Just [like TestData.load](#loading-without-transactions), you can call
1084
+ `TestData.truncate` when `use_transactional_data_loader` is `false` and it will
1085
+ commit the truncation.
625
1086
 
626
- * If a test calls `TestData.truncate` and the `:after_data_truncate` savepoint
627
- is rolled back outside the `TestData.rollback` method (e.g.
628
- `ActiveRecord::Base.connection.rollback_transaction`), the method will first
629
- check a memo written to `ar_internal_metadata` and recreate the
630
- `:after_data_truncate` savepoint
1087
+ ### TestData.prevent_rails_fixtures_from_loading_automatically!
631
1088
 
632
- #### If you're not using transactions
1089
+ Call this method before any tests have been loaded or executed by your test
1090
+ runner if you're planning to use
1091
+ [TestData.load_rails_fixtures](#testdataload_rails_fixtures) to load Rails
1092
+ fixtures into any of your tests. This method will disable the default behavior
1093
+ of loading your Rails fixtures into the test database as soon as the first test
1094
+ case with fixtures enabled is executed. (Inspect the [source for the
1095
+ patch](/lib/test_data/active_record_ext.rb) to make sure you're comfortable with
1096
+ what it's doing.)
633
1097
 
634
- Just [like TestData.load](#loading-without-transactions), you can call
635
- `TestData.truncate(transactions: false)` and it'll commit the truncation.
1098
+ ### TestData.load_rails_fixtures
1099
+
1100
+ As described in this README's [fixture interop
1101
+ guide](#getting-your-fixtures-dependent-tests-passing-with-test_data),
1102
+ `TestData.load_rails_fixtures` will load your app's [Rails
1103
+ fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures)
1104
+ into an effectively empty test database inside a nested transaction.
1105
+
1106
+ Using this feature requires that you've:
1107
+
1108
+ 1. Invoked
1109
+ [TestData.prevent_rails_fixtures_from_loading_automatically!](#testdataprevent_rails_fixtures_from_loading_automatically)
1110
+ 2. Have `config.use_transactional_data_loader` set to true (the default) in your
1111
+ [config](#testdataconfig)
1112
+
1113
+ When you call this method, it will do the following:
1114
+
1115
+ 1. Verify your `test_data` dump has been loaded (or else load it)
1116
+ 2. Verify the loaded data has been truncated (or else truncate it)
1117
+ 3. Load your Rails fixtures from their YAML source files into your test database
1118
+ 4. Create a new savepoint in the nested transactions named
1119
+ `:after_load_rails_fixtures`
1120
+
1121
+ Once loaded, your tests will be able to use your test fixtures inside a
1122
+ transaction. At teardown-time, you can reset those fixtures by rolling back with
1123
+ [TestData.rollback(:after_load_rails_fixtures)](#rolling-back-to-after-rails-fixtures-were-loaded).
636
1124
 
637
1125
  ## Assumptions
638
1126
 
@@ -643,15 +1131,16 @@ yet. Here are some existing assumptions and limitations:
643
1131
 
644
1132
  * You're using Rails 6 or higher
645
1133
 
646
- * Your app does not rely on Rails' [multi-database
1134
+ * Your app does not require Rails' [multi-database
647
1135
  support](https://guides.rubyonrails.org/active_record_multiple_databases.html)
1136
+ in order to be tested
648
1137
 
649
1138
  * Your app has the binstubs `bin/rake` and `bin/rails` that Rails generates and
650
- they work (you can regenerate them with `rails app:update:bin`)
1139
+ they work (protip: you can regenerate them with `rails app:update:bin`)
651
1140
 
652
1141
  * Your `database.yml` defines a `&default` alias from which to extend the
653
- `test_data` database configuration (if it lacks one, you'll need specify the
654
- database's configuration on your own)
1142
+ `test_data` database configuration (if your YAML file lacks one, you can
1143
+ always specify the `test_data` database configuration manually)
655
1144
 
656
1145
  ## Fears, Uncertainties, and Doubts
657
1146
 
@@ -672,7 +1161,7 @@ If you use `factory_bot` and all of these are true:
672
1161
  menu
673
1162
 
674
1163
  * Your default factories generate models that resemble real records created by
675
- your production application, as opposed to resembling the
1164
+ your production application, as opposed to representing the
676
1165
  sum-of-all-edge-cases with every boolean flag enabled and optional attribute
677
1166
  set
678
1167
 
@@ -690,7 +1179,7 @@ designed to address. We hope you'll consider trying it with an open mind. At the
690
1179
  same time, we acknowledge that large test suites can't be rewritten and migrated
691
1180
  to a different source of test data overnight—nor should they be! See our notes
692
1181
  on [migrating to `test_data`
693
- incrementally](#we-already-have-thousands-of-tests-that-depend-on-rails-fixtures-or-factory_bot-can-we-start-using-test_data-without-throwing-them-away-and-starting-over)
1182
+ incrementally](#factory--fixture-interoperability-guide)
694
1183
 
695
1184
  ### How will I handle merge conflicts in these SQL files if I have lots of people working on lots of feature branches all adding to the `test_data` database dumps?
696
1185
 
@@ -717,124 +1206,6 @@ iterate than detect bugs. Once you you have a reasonably stable feature working
717
1206
  end-to-end, that's a good moment to start adding integration tests (and thus
718
1207
  pulling in a test data gem like this one to help you).
719
1208
 
720
- ### We already have thousands of tests that depend on Rails fixtures or factory_bot; can we start using this gem without throwing them away and starting over?
721
-
722
- Yes, `test_data` was written with this sort of incremental transition in mind!
723
-
724
- Our strongest recommendation is to write each test against only a single source
725
- of test data. Integration tests inevitably become coupled to the data that's
726
- available to them, and if a test has access to fixtures, records created by a
727
- factory, and a `test_data` SQL dump, it is likely to unintentionally become
728
- dependent on all three. This could result in the test having more ways to fail
729
- than necessary and make it harder to simplify your test data strategy later.
730
- Instead, consider explicitly opting into a single type of test data for each
731
- test.
732
-
733
- The rest comes down to how you want to organize your tests, as discussed below.
734
-
735
- #### Approach 1: test-data-specific test subclasses
736
-
737
- If you're currently using `factory_bot`, you might already have something like
738
- this in your `test_helper.rb` file:
739
-
740
- ```ruby
741
- class ActiveSupport::TestCase
742
- include FactoryBot::Syntax::Methods
743
- end
744
- ```
745
-
746
- To avoid including these methods with every Rails-aware test, you might consider
747
- pushing down new classes for each source of test data:
748
-
749
- ```ruby
750
- # Tests using data created by `test_data`
751
- class TestDataTestCase < ActiveSupport::TestCase
752
- def setup
753
- TestData.load
754
- end
755
-
756
- def teardown
757
- TestData.rollback
758
- end
759
- end
760
-
761
- # Tests using data created by `factory_bot`
762
- class FactoryBotTestCase < ActiveSupport::TestCase
763
- include FactoryBot::Syntax::Methods
764
-
765
- def setup
766
- TestData.truncate
767
- end
768
-
769
- def teardown
770
- TestData.rollback(:after_data_truncate)
771
- end
772
- end
773
- ```
774
-
775
- From there, the class that each test extends will indicate which test data
776
- strategy it uses.
777
-
778
- This approach may work in simple cases, but won't be well-suited to if you're
779
- already extending any of the half-dozen subclasses of `ActiveSupport::TestCase`
780
- provided by Rails (or their analogues from
781
- [rspec-rails](https://github.com/rspec/rspec-rails)).
782
-
783
- #### Approach 2: write a helper method to set things up for you
784
-
785
- Every situation will be different, but one strategy that suits a lot of
786
- circumstances would be to write a method to declare and configure the test data
787
- strategy for the current test.
788
-
789
- Taking from [this
790
- example](/example/test/integration/better_mode_switching_demo_test.rb) test, you
791
- could implement a class method like this:
792
-
793
- ```ruby
794
- class ActiveSupport::TestCase
795
- def self.test_data_mode(mode)
796
- case mode
797
- when :factory_bot
798
- require "factory_bot_rails"
799
- include FactoryBot::Syntax::Methods
800
-
801
- setup do
802
- TestData.truncate
803
- end
804
-
805
- teardown do
806
- TestData.rollback(:after_data_truncate)
807
- end
808
- when :test_data
809
- setup do
810
- TestData.load
811
- end
812
-
813
- teardown do
814
- TestData.rollback
815
- end
816
- end
817
- end
818
- end
819
- ```
820
-
821
- And then (without any class inheritance complications), simply declare which
822
- kind of test you're specifying:
823
-
824
- ```ruby
825
- class SomeFactoryUsingTest < ActiveSupport::TestCase
826
- test_data_mode :factory_bot
827
-
828
- # … tests go here
829
- end
830
-
831
- class SomeTestDataUsingTest < ActionDispatch::IntegrationTest
832
- test_data_mode :test_data
833
-
834
- # etc.
835
- end
836
- ```
837
-
838
1209
  ### Why can't I save multiple database dumps to cover different scenarios?
839
1210
 
840
1211
  For the same reason you (probably) don't have multiple production databases: the
@@ -854,7 +1225,8 @@ production itself.
854
1225
 
855
1226
  ### Are you sure I should commit these SQL dumps? They're way too big!
856
1227
 
857
- If the dump files generated by `test_data:dump` are absolutely massive, consider the cause:
1228
+ If the dump files generated by `test_data:dump` seem massive, consider the
1229
+ cause:
858
1230
 
859
1231
  1. If you inadvertently created more data than necessary, you might consider
860
1232
  resetting (or rolling back) your changes and making another attempt at
@@ -867,7 +1239,9 @@ If the dump files generated by `test_data:dump` are absolutely massive, consider
867
1239
  where they'd still be committed to git, but won't loaded by your tests
868
1240
 
869
1241
  * Exclude data from those tables entirely by adding them to the
870
- `config.dont_dump_these_tables` array
1242
+ `config.dont_dump_these_tables` array. (Note that `rake test_data:load`
1243
+ won't be able to restore these tables into your `test_data` environment,
1244
+ so if the data is needed for the app to operate, you'll need to dump them)
871
1245
 
872
1246
  3. If the dumps are _necessarily_ really big (some apps are complex!), consider
873
1247
  looking into [git-lfs](https://git-lfs.github.com) for tracking them without
@@ -903,7 +1277,7 @@ This test is simple, self-contained, clearly denotes
903
1277
  [arrange-act-assert](https://github.com/testdouble/contributing-tests/wiki/Arrange-Act-Assert),
904
1278
  and (most importantly) will only fail if the functionality stops working.
905
1279
  Maximizing the number of tests that can be written expressively and succinctly
906
- without the aid of an external helper is a laudable goal that more teams should
1280
+ without the aid of shared test data is a laudable goal that more teams should
907
1281
  embrace.
908
1282
 
909
1283
  However, what if the code you're writing doesn't need 3 records in the database,
@@ -913,8 +1287,10 @@ point, you have two options:
913
1287
 
914
1288
  1. Critically validate your design: why is it so hard to set up? Does it
915
1289
  _really_ require so much persisted data to exercise this behavior? Would a
916
- plain old Ruby object that defined a pure function have been feasible? Could
917
- a model instance or even a `Struct` be passed to the
1290
+ [plain old Ruby
1291
+ object](https://steveklabnik.com/writing/the-secret-to-rails-oo-design) that
1292
+ defined a pure function have been feasible? Could a model instance or even a
1293
+ `Struct` be passed to the
918
1294
  [subject](https://github.com/testdouble/contributing-tests/wiki/Subject)
919
1295
  instead of loading everything from the database? When automated testing is
920
1296
  saved for the very end of a feature's development, it can feel too costly to
@@ -928,9 +1304,9 @@ point, you have two options:
928
1304
 
929
1305
  As a result, there is no one-size-fits-all approach. Straightforward behavior
930
1306
  that can be invoked with a clear, concise test has no reason to be coupled to a
931
- shared source of test data. Subtle behavior that requires lots of data to be
932
- exercised would see its tests grow unwieldy without something to help populate
933
- that data. So both kinds of test clearly have their place.
1307
+ shared source of test data. Subtle behavior that requires lots of
1308
+ carefully-arranged data would see its tests grow unwieldy without something to
1309
+ help populate that data. So both kinds of test clearly have their place.
934
1310
 
935
1311
  But this is a pretty nuanced discussion that can be hard to keep in mind when
936
1312
  under deadline pressure or on a large team where building consensus around norms
@@ -944,21 +1320,21 @@ integration tests, but not for basic tests of models, like so:
944
1320
 
945
1321
  ```ruby
946
1322
  class ActionDispatch::IntegrationTest
947
- def setup
1323
+ setup do
948
1324
  TestData.load
949
1325
  end
950
1326
 
951
- def teardown
1327
+ teardown do
952
1328
  TestData.rollback
953
1329
  end
954
1330
  end
955
1331
 
956
1332
  class ActiveSupport::TestCase
957
- def setup
1333
+ setup do
958
1334
  TestData.truncate
959
1335
  end
960
1336
 
961
- def teardown
1337
+ teardown do
962
1338
  TestData.rollback(:after_data_truncate)
963
1339
  end
964
1340
  end