test_data 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +5 -5
- data/README.md +129 -37
- data/example/Gemfile.lock +1 -1
- data/example/test/integration/fixture_load_count_test.rb +82 -0
- data/lib/test_data/custom_loaders/rails_fixtures.rb +6 -3
- data/lib/test_data/version.rb +1 -1
- data/script/test +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18d3469e9d0173f81ef3b2d8d082861094d79354b8fb544e983bdcfdd8eb788b
|
4
|
+
data.tar.gz: 983a1645132b22024799670ec463f11fe5f8e0d1b9427b96940dea8c684fbafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7fb1b86f23f074bb185cb1cf11c437d4ba57445d65907509384838993628c5759803ee90f3f290dbb219754806bb1c1133b7e84bf430918c1a23b3fca86b752
|
7
|
+
data.tar.gz: d00e512b415c74ab1650a3b3d58e36759d085056375e07326d8790d0033bc18066a2d82c791a578fb9d11cb7c08f9056d4d5e380b9cc52f97dd467c245469771
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
test_data (0.2.
|
4
|
+
test_data (0.2.2)
|
5
5
|
railties (~> 6.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -34,14 +34,14 @@ GEM
|
|
34
34
|
erubi (1.10.0)
|
35
35
|
i18n (1.8.10)
|
36
36
|
concurrent-ruby (~> 1.0)
|
37
|
-
loofah (2.
|
37
|
+
loofah (2.11.0)
|
38
38
|
crass (~> 1.0.2)
|
39
39
|
nokogiri (>= 1.5.9)
|
40
40
|
method_source (1.0.0)
|
41
|
-
mini_portile2 (2.
|
41
|
+
mini_portile2 (2.6.1)
|
42
42
|
minitest (5.14.4)
|
43
|
-
nokogiri (1.
|
44
|
-
mini_portile2 (~> 2.
|
43
|
+
nokogiri (1.12.0)
|
44
|
+
mini_portile2 (~> 2.6.1)
|
45
45
|
racc (~> 1.4)
|
46
46
|
parallel (1.20.1)
|
47
47
|
parser (3.0.1.0)
|
data/README.md
CHANGED
@@ -375,17 +375,30 @@ RSpec.describe "Kitchen sink", type: :request do
|
|
375
375
|
end
|
376
376
|
```
|
377
377
|
|
378
|
+
But wait, there's more! If your test suite switches between multiple modes from
|
379
|
+
test-to-test, it's important to be aware of the marginal cost _between_ each of
|
380
|
+
those tests. For example, two tests in a row that call `TestData.uses_test_data`
|
381
|
+
only need a simple rollback as test setup, but a `TestData.uses_test_data`
|
382
|
+
followed by a `TestData.uses_clean_slate` requires a rollback, a truncation, and
|
383
|
+
another savepoint. These small costs add up, so consider [speeding up your
|
384
|
+
build](#im-worried-my-tests-arent-as-fast-as-they-should-be) by grouping your
|
385
|
+
tests into sub-suites based on their source of test data.
|
386
|
+
|
378
387
|
#### If your situation is more complicated
|
379
388
|
|
380
389
|
If you're adding `test_data` to an existing application, it's likely that you
|
381
390
|
won't be able to easily adopt a one-size-fits-all approach to test setup across
|
382
391
|
your entire suite. Some points of reference, if that's the situation you're in:
|
383
392
|
|
384
|
-
* If your test suite is already using fixtures or factories and the above
|
385
|
-
just broke everything, check out our [interoperability
|
393
|
+
* If your test suite is **already using fixtures or factories** and the above
|
394
|
+
hooks just broke everything, check out our [interoperability
|
386
395
|
guide](#factory--fixture-interoperability-guide) for help.
|
387
|
-
* If you
|
388
|
-
|
396
|
+
* If you need to make any changes to the data after it's loaded, truncated, or
|
397
|
+
after Rails fixtures are loaded, you can configure [lifecycle
|
398
|
+
hooks](#lifecycle-hooks) that will help you achieve a **very fast test suite**
|
399
|
+
by including those changes inside the transaction savepoints
|
400
|
+
* If you **don't want `test_data` managing transactions** and cleanup for you
|
401
|
+
and just want to load the SQL dump, you can call
|
389
402
|
[TestData.insert_test_data_dump](#testdatainsert_test_data_dump)
|
390
403
|
* For more information on how all this works, see the [API
|
391
404
|
reference](#api-reference).
|
@@ -489,15 +502,15 @@ suite after following the initial setup guide and see if the suite just passes.
|
|
489
502
|
|
490
503
|
If you find that your test suite is failing after adding
|
491
504
|
`TestData.uses_test_data` to your setup, don't panic! Test failures are most
|
492
|
-
likely caused by the combination of your `test_data`
|
493
|
-
|
505
|
+
likely caused by the combination of your `test_data` SQL dump with the records
|
506
|
+
inserted by your factories.
|
494
507
|
|
495
508
|
One approach would be to attempt to resolve each such failure one-by-one—usually
|
496
509
|
by updating the offending factories or editing your `test_data` database to
|
497
510
|
ensure they steer clear of one another. Care should be taken to preserve the
|
498
|
-
conceptual encapsulation of each test, however, as naively squashing errors
|
499
|
-
|
500
|
-
|
511
|
+
conceptual encapsulation of each test, however, as naively squashing errors
|
512
|
+
risks introducing inadvertent coupling between your factories and your
|
513
|
+
`test_data` data such that neither can be used independently of the other.
|
501
514
|
|
502
515
|
Another approach that the `test_data` gem provides is an additional mode with
|
503
516
|
`TestData.uses_clean_slate`, which—when called at the top of a factory-dependent
|
@@ -1203,8 +1216,113 @@ run. That said—and especially if you're adding `test_data` to an existing test
|
|
1203
1216
|
suite—care should be taken to audit everything the suite does between tests in
|
1204
1217
|
order to optimize its overall runtime.
|
1205
1218
|
|
1206
|
-
|
1207
|
-
|
1219
|
+
#### Randomized test order leading to data churn
|
1220
|
+
|
1221
|
+
Generally speaking, randomizing the order in which tests run is an unmitigated
|
1222
|
+
win: randomizing helps you catch any unintended dependency between two tests
|
1223
|
+
early, when it's still cheap & easy to fix. However, if your tests use different
|
1224
|
+
sources of test data (e.g. some call `TestData.uses_test_data` and some call
|
1225
|
+
`TestData.uses_clean_slate`), it's very likely that randomizing your tests will
|
1226
|
+
result in a significantly slower overall test suite. Instead, if you group tests
|
1227
|
+
that use the same type of test data together (e.g. by separating them into
|
1228
|
+
separate suites), you might find profound speed gains.
|
1229
|
+
|
1230
|
+
To illustrate why, suppose you have 5 tests that call `TestData.uses_test_data`
|
1231
|
+
and 5 that call `TestData.uses_rails_fixtures`. If a test that calls
|
1232
|
+
`TestData.uses_test_data` is followed by another that calls `uses_test_data`,
|
1233
|
+
the only operation needed by the second call will be a rollback to the savepoint
|
1234
|
+
taken after the test data was loaded. If, however, a `uses_test_data` test is
|
1235
|
+
followed by a `uses_rails_fixtures` test, then a lot more work is required:
|
1236
|
+
first a rollback, then the truncation of the test data, then a load of the
|
1237
|
+
fixtures followed by creation of a new savepoint—which would in tunr be undone
|
1238
|
+
again if the _next_ test happened to call `uses_test_data`. Switching between
|
1239
|
+
tests that use different sources of test data can cause significant unnecessary
|
1240
|
+
thrashing.
|
1241
|
+
|
1242
|
+
To illustrate the above, if all of these tests ran in random order (the
|
1243
|
+
default), you might see:
|
1244
|
+
|
1245
|
+
```
|
1246
|
+
$ bin/rails test test/example_test.rb
|
1247
|
+
Run options: --seed 63999
|
1248
|
+
|
1249
|
+
# Running:
|
1250
|
+
|
1251
|
+
test_data -- loading test_data SQL dump
|
1252
|
+
. fixtures -- truncating tables, loading Rails fixtures
|
1253
|
+
. fixtures -- rolling back to Rails fixtures
|
1254
|
+
. test_data -- rolling back to clean test_data
|
1255
|
+
. fixtures -- truncating tables, loading Rails fixtures
|
1256
|
+
. test_data -- rolling back to clean test_data
|
1257
|
+
. fixtures -- truncating tables, loading Rails fixtures
|
1258
|
+
. test_data -- rolling back to clean test_data
|
1259
|
+
. fixtures -- truncating tables, loading Rails fixtures
|
1260
|
+
. test_data -- rolling back to clean test_data
|
1261
|
+
.
|
1262
|
+
|
1263
|
+
Finished in 2.449957s, 4.0817 runs/s, 4.0817 assertions/s.
|
1264
|
+
10 runs, 10 assertions, 0 failures, 0 errors, 0 skips
|
1265
|
+
```
|
1266
|
+
|
1267
|
+
So, what can you do to speed this up? The most effective strategy to avoiding
|
1268
|
+
this churn is to group the execution of each tests that use each source of test
|
1269
|
+
data into sub-suites that are run serially, on e after the other.
|
1270
|
+
|
1271
|
+
* If you're using Rails' defualt Minitest, we wrote a gem called
|
1272
|
+
[minitest-suite](https://github.com/testdouble/minitest-suite) to accomplish
|
1273
|
+
exactly this. Just declare something like `suite :test_data` or `suite
|
1274
|
+
:fixtures` at the top of each test class
|
1275
|
+
* If you're using RSpec, the
|
1276
|
+
[tag](https://relishapp.com/rspec/rspec-core/v/3-10/docs/command-line/tag-option)
|
1277
|
+
feature can help you organize your tests by type, but you'll likely have to
|
1278
|
+
run a separate CLI invocation for each to avoid the tests from being
|
1279
|
+
interleaved
|
1280
|
+
|
1281
|
+
Here's what the same example would do at run-time after adding
|
1282
|
+
[minitest-suite](https://github.com/testdouble/minitest-suite):
|
1283
|
+
|
1284
|
+
```
|
1285
|
+
$ bin/rails test test/example_test.rb
|
1286
|
+
Run options: --seed 50105
|
1287
|
+
|
1288
|
+
# Running:
|
1289
|
+
|
1290
|
+
test_data -- loading test_data SQL dump
|
1291
|
+
. test_data -- rolling back to clean test_data
|
1292
|
+
. test_data -- rolling back to clean test_data
|
1293
|
+
. test_data -- rolling back to clean test_data
|
1294
|
+
. test_data -- rolling back to clean test_data
|
1295
|
+
. fixtures -- truncating tables, loading Rails fixtures
|
1296
|
+
. fixtures -- rolling back to clean fixtures
|
1297
|
+
. fixtures -- rolling back to clean fixtures
|
1298
|
+
. fixtures -- rolling back to clean fixtures
|
1299
|
+
. fixtures -- rolling back to clean fixtures
|
1300
|
+
.
|
1301
|
+
|
1302
|
+
Finished in 2.377050s, 4.2069 runs/s, 4.2069 assertions/s.
|
1303
|
+
10 runs, 10 assertions, 0 failures, 0 errors, 0 skips
|
1304
|
+
```
|
1305
|
+
|
1306
|
+
By grouping the execution in this way, the most expensive operations will
|
1307
|
+
usually only be run once: at the beginning of the first test in each suite.
|
1308
|
+
|
1309
|
+
#### Expensive data manipulation
|
1310
|
+
|
1311
|
+
If you're doing anything repeatedly that's data-intensive in your test setup
|
1312
|
+
after calling one of the `TestData.uses_*` methods, that operation is being
|
1313
|
+
repeated once per test, which could be very slow. Instead, you might consider
|
1314
|
+
moving that behavior into a [lifecycle hook](#lifecycle-hooks).
|
1315
|
+
|
1316
|
+
Any code passed to a lifecycle hook will only be executed when data is
|
1317
|
+
_actually_ loaded or truncated and its effect will be included in the
|
1318
|
+
transaction savepoint that the `test_data` gem rolls back between tests.
|
1319
|
+
Seriously, appropriately moving data adjustments into these hooks can cut your
|
1320
|
+
test suite's runtime by an order of magnitude.
|
1321
|
+
|
1322
|
+
#### Redundant test setup tasks
|
1323
|
+
|
1324
|
+
One of the most likely sources of unnecessary slowness is redundant test
|
1325
|
+
cleanup. The speed gained from sandwiching every expensive operation between
|
1208
1326
|
transaction savepoints can be profound… but can also easily be erased by a
|
1209
1327
|
single before-each hook calling
|
1210
1328
|
[database_cleaner](https://github.com/DatabaseCleaner/database_cleaner) to
|
@@ -1213,32 +1331,6 @@ time to take stock of everything that's called between tests during setup &
|
|
1213
1331
|
teardown to ensure multiple tools aren't attempting to clean up the state of the
|
1214
1332
|
database and potentially interfering with one another.
|
1215
1333
|
|
1216
|
-
A second opportunity for optimization is to group tests that use the same type
|
1217
|
-
of test data together, either into separate suites or by preventing them from
|
1218
|
-
being run in random order across said types. For example, suppose you have 10
|
1219
|
-
tests that call `TestData.uses_test_data` and 10 that call
|
1220
|
-
`TestData.uses_rails_fixtures`. If a test that calls `TestData.uses_test_data`
|
1221
|
-
is followed by another that calls `uses_test_data`, the only operation needed by
|
1222
|
-
the second call will be a rollback to the savepoint taken after the test data
|
1223
|
-
was loaded. If, however, a `uses_test_data` test is followed by a
|
1224
|
-
`uses_rails_fixtures` test, then the test data will be truncated and the
|
1225
|
-
fixtures loaded and new savepoints created (which would then be undone again if
|
1226
|
-
the _next_ test happened to call `uses_test_data`).
|
1227
|
-
|
1228
|
-
As a result of the above, the marginal runtime cost for each `TestData.uses_*`
|
1229
|
-
method depends on which kinds of test precedes and follows it. That means your
|
1230
|
-
tests will run faster overall if the tests that call `TestData.uses_test_data`
|
1231
|
-
are run as a group separately from your tests that rely on
|
1232
|
-
`TestData.uses_clean_slate` or `TestData.uses_rails_fixtures`. Separating your
|
1233
|
-
tests into logical groups pretty trivial if you're using RSpec, as the
|
1234
|
-
[tag](https://relishapp.com/rspec/rspec-core/v/3-10/docs/command-line/tag-option)
|
1235
|
-
feature was built with this sort of need in mind. If you're using Minitest, you
|
1236
|
-
might consider organizing the tests in different directories and running
|
1237
|
-
multiple commands to execute them (e.g. `bin/rails test test/test_data_tests`
|
1238
|
-
and `bin/rails test/factory_tests`). Every CI configuration is different,
|
1239
|
-
however, and you may find yourself needing to get creative in configuring things
|
1240
|
-
to achieve the fastest build time.
|
1241
|
-
|
1242
1334
|
## Code of Conduct
|
1243
1335
|
|
1244
1336
|
This project follows Test Double's [code of
|
data/example/Gemfile.lock
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Regression test to make sure we don't load fixtures too many times
|
2
|
+
|
3
|
+
class HookCounter
|
4
|
+
def self.count
|
5
|
+
@call_count || 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.count!
|
9
|
+
@call_count ||= 0
|
10
|
+
@call_count += 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
at_exit do
|
14
|
+
if TestData.statistics.load_rails_fixtures_count > 2 # could be 1 if :all runs first, 2 if :boops only does
|
15
|
+
raise "Rails fixture load was called #{TestData.statistics.load_rails_fixtures_count} times, shouldn't be more than 2!"
|
16
|
+
end
|
17
|
+
if HookCounter.count > 2
|
18
|
+
raise "Rails fixture load hook was called #{HookCounter.count} times, shouldn't be more than 2!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require "test_helper"
|
23
|
+
|
24
|
+
TestData.prevent_rails_fixtures_from_loading_automatically!
|
25
|
+
|
26
|
+
TestData.config do |config|
|
27
|
+
config.after_rails_fixture_load {
|
28
|
+
HookCounter.count!
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
class PartialFixtureTest < ActiveSupport::TestCase
|
33
|
+
fixtures :boops
|
34
|
+
|
35
|
+
setup do
|
36
|
+
TestData.uses_rails_fixtures(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_has_only_boops
|
40
|
+
assert boops(:boop_1)
|
41
|
+
assert_raises(NameError) { method(:pants) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class AllFixtureTest < ActiveSupport::TestCase
|
46
|
+
fixtures :all
|
47
|
+
|
48
|
+
setup do
|
49
|
+
TestData.uses_rails_fixtures(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_has_both
|
53
|
+
assert boops(:boop_1)
|
54
|
+
assert pants(:pant_1)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class AllFixtureTest2 < ActiveSupport::TestCase
|
59
|
+
fixtures :all
|
60
|
+
|
61
|
+
setup do
|
62
|
+
TestData.uses_rails_fixtures(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_has_both
|
66
|
+
assert boops(:boop_1)
|
67
|
+
assert pants(:pant_1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class AllFixtureTest3 < ActiveSupport::TestCase
|
72
|
+
fixtures :all
|
73
|
+
|
74
|
+
setup do
|
75
|
+
TestData.uses_rails_fixtures(self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_has_both
|
79
|
+
assert boops(:boop_1)
|
80
|
+
assert pants(:pant_1)
|
81
|
+
end
|
82
|
+
end
|
@@ -21,19 +21,22 @@ module TestData
|
|
21
21
|
|
22
22
|
def load_requested(test_instance:)
|
23
23
|
ActiveRecord::FixtureSet.reset_cache
|
24
|
-
test_instance.instance_variable_set(:@loaded_fixtures,
|
24
|
+
test_instance.instance_variable_set(:@loaded_fixtures,
|
25
|
+
@already_loaded_rails_fixtures.slice(*test_instance.class.fixture_table_names))
|
25
26
|
test_instance.instance_variable_set(:@fixture_cache, {})
|
26
27
|
end
|
27
28
|
|
28
29
|
def loaded?(test_instance:)
|
29
|
-
|
30
|
+
test_instance.class.fixture_table_names.all? { |table_name|
|
31
|
+
@already_loaded_rails_fixtures.key?(table_name)
|
32
|
+
}
|
30
33
|
end
|
31
34
|
|
32
35
|
def load(test_instance:)
|
33
36
|
test_instance.pre_loaded_fixtures = false
|
34
37
|
test_instance.use_transactional_tests = false
|
35
38
|
test_instance.__test_data_gem_setup_fixtures
|
36
|
-
@already_loaded_rails_fixtures
|
39
|
+
@already_loaded_rails_fixtures = test_instance.instance_variable_get(:@loaded_fixtures)
|
37
40
|
@statistics.count_load_rails_fixtures!
|
38
41
|
@config.after_rails_fixture_load_hook.call
|
39
42
|
end
|
data/lib/test_data/version.rb
CHANGED
data/script/test
CHANGED
@@ -52,6 +52,7 @@ bin/rails test test/integration/transaction_committing_boops_test.rb
|
|
52
52
|
# Run a test that prevents Rails fixtures for preloading and then loads them in a transaction
|
53
53
|
bin/rails test test/integration/rails_fixtures_override_test.rb
|
54
54
|
bundle exec rspec spec/requests/rails_fixtures_override_spec.rb
|
55
|
+
bin/rails test test/integration/fixture_load_count_test.rb
|
55
56
|
|
56
57
|
# Run a test that forgets to prevent Rails fixtures but then tries to load them in a transaction
|
57
58
|
bin/rails test test/integration/rails_fixtures_double_load_test.rb
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test_data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- example/test/integration/better_mode_switching_demo_test.rb
|
104
104
|
- example/test/integration/boops_that_boop_boops_test.rb
|
105
105
|
- example/test/integration/dont_dump_tables_test.rb
|
106
|
+
- example/test/integration/fixture_load_count_test.rb
|
106
107
|
- example/test/integration/load_rollback_truncate_test.rb
|
107
108
|
- example/test/integration/migrated_boops_test.rb
|
108
109
|
- example/test/integration/mode_switching_demo_test.rb
|