test_data 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -5
  3. data/.standard.yml +2 -0
  4. data/CHANGELOG.md +38 -1
  5. data/Gemfile.lock +15 -15
  6. data/LICENSE.txt +1 -1
  7. data/README.md +701 -712
  8. data/example/.gitignore +1 -4
  9. data/example/Gemfile.lock +1 -1
  10. data/example/config/application.rb +3 -0
  11. data/example/config/credentials.yml.enc +1 -2
  12. data/example/spec/rails_helper.rb +1 -1
  13. data/example/spec/requests/boops_spec.rb +1 -5
  14. data/example/spec/requests/rails_fixtures_override_spec.rb +106 -0
  15. data/example/test/integration/better_mode_switching_demo_test.rb +2 -10
  16. data/example/test/integration/fixture_load_count_test.rb +82 -0
  17. data/example/test/integration/load_rollback_truncate_test.rb +40 -45
  18. data/example/test/integration/mode_switching_demo_test.rb +4 -14
  19. data/example/test/integration/parallel_boops_with_fixtures_test.rb +1 -5
  20. data/example/test/integration/parallel_boops_without_fixtures_test.rb +1 -5
  21. data/example/test/integration/rails_fixtures_double_load_test.rb +2 -2
  22. data/example/test/integration/rails_fixtures_override_test.rb +18 -35
  23. data/example/test/integration/test_data_hooks_test.rb +89 -0
  24. data/example/test/integration/transaction_committing_boops_test.rb +1 -10
  25. data/example/test/test_helper.rb +1 -5
  26. data/lib/generators/test_data/environment_file_generator.rb +4 -0
  27. data/lib/generators/test_data/initializer_generator.rb +19 -13
  28. data/lib/test_data/config.rb +30 -12
  29. data/lib/test_data/custom_loaders/abstract_base.rb +25 -0
  30. data/lib/test_data/custom_loaders/rails_fixtures.rb +45 -0
  31. data/lib/test_data/detects_database_existence.rb +19 -0
  32. data/lib/test_data/determines_databases_associated_dump_time.rb +13 -0
  33. data/lib/test_data/determines_when_sql_dump_was_made.rb +24 -0
  34. data/lib/test_data/dumps_database.rb +3 -0
  35. data/lib/test_data/inserts_test_data.rb +25 -0
  36. data/lib/test_data/manager.rb +187 -0
  37. data/lib/test_data/railtie.rb +4 -0
  38. data/lib/test_data/rake.rb +41 -12
  39. data/lib/test_data/records_dump_metadata.rb +9 -0
  40. data/lib/test_data/truncates_test_data.rb +31 -0
  41. data/lib/test_data/version.rb +1 -1
  42. data/lib/test_data/warns_if_database_is_newer_than_dump.rb +32 -0
  43. data/lib/test_data/warns_if_dump_is_newer_than_database.rb +36 -0
  44. data/lib/test_data.rb +43 -1
  45. data/script/reset_example_app +1 -0
  46. data/script/test +54 -6
  47. metadata +17 -3
  48. data/lib/test_data/transactional_data_loader.rb +0 -300
@@ -1,29 +1,19 @@
1
1
  require "test_helper"
2
2
 
3
3
  class ModeSwitchingTestCase < ActiveSupport::TestCase
4
+ self.use_transactional_tests = false
5
+
4
6
  def self.test_data_mode(mode)
5
7
  if mode == :factory_bot
6
8
  require "factory_bot_rails"
7
9
  include FactoryBot::Syntax::Methods
8
10
 
9
11
  setup do
10
- TestData.rollback(:before_data_load)
11
- ActiveRecord::Base.connection.begin_transaction(joinable: false, _lazy: false)
12
- end
13
-
14
- teardown do
15
- ActiveRecord::Base.connection.rollback_transaction
12
+ TestData.uses_clean_slate
16
13
  end
17
-
18
14
  elsif mode == :test_data
19
- self.use_transactional_tests = false
20
-
21
15
  setup do
22
- TestData.load
23
- end
24
-
25
- teardown do
26
- TestData.rollback
16
+ TestData.uses_test_data
27
17
  end
28
18
  end
29
19
  end
@@ -6,11 +6,7 @@ class ParallelizedTransactionalFixturefullTestCase < ActiveSupport::TestCase
6
6
  fixtures :all
7
7
 
8
8
  setup do
9
- TestData.load
10
- end
11
-
12
- teardown do
13
- TestData.rollback
9
+ TestData.uses_test_data
14
10
  end
15
11
  end
16
12
 
@@ -5,11 +5,7 @@ class ParallelizedNonTransactionalFixturelessTestCase < ActiveSupport::TestCase
5
5
  self.use_transactional_tests = false
6
6
 
7
7
  setup do
8
- TestData.load
9
- end
10
-
11
- teardown do
12
- TestData.rollback
8
+ TestData.uses_test_data
13
9
  end
14
10
  end
15
11
 
@@ -3,8 +3,8 @@ require "test_helper"
3
3
  class FixturesUsingTest < ActiveSupport::TestCase
4
4
  def test_tries_to_load_rails_fixtures_with_test_data
5
5
  error = assert_raises(TestData::Error) do
6
- TestData.load_rails_fixtures(self)
6
+ TestData.uses_rails_fixtures(self)
7
7
  end
8
- assert_match "'TestData.load_rails_fixtures' depends on Rails' default fixture-loading behavior being disabled by calling 'TestData.prevent_rails_fixtures_from_loading_automatically!' as early as possible (e.g. near the top of your test_helper.rb), but it looks like it was never called", error.message
8
+ assert_match "'TestData.uses_rails_fixtures(self)' depends on Rails' default fixture-loading behavior being disabled by calling 'TestData.prevent_rails_fixtures_from_loading_automatically!' as early as possible (e.g. near the top of your test_helper.rb), but it looks like it was never called", error.message
9
9
  end
10
10
  end
@@ -6,26 +6,23 @@ class FixtureFreeTestData < ActiveSupport::TestCase
6
6
  fixtures :boops # why not
7
7
 
8
8
  setup do
9
- TestData.load
9
+ TestData.uses_test_data
10
10
  end
11
11
 
12
12
  def test_has_no_fixture_boops
13
13
  assert_equal 15, Boop.count
14
14
  end
15
-
16
- teardown do
17
- TestData.rollback
18
- end
19
15
  end
20
16
 
21
17
  class FixturesUsingTest < ActiveSupport::TestCase
22
18
  fixtures :boops
23
19
 
24
20
  setup do
25
- TestData.load_rails_fixtures(self)
21
+ TestData.uses_rails_fixtures(self)
26
22
  end
27
23
 
28
24
  def test_has_fixture_boops
25
+ assert boops(:boop_1).persisted?
29
26
  assert_equal 2, Boop.count
30
27
  end
31
28
 
@@ -34,29 +31,25 @@ class FixturesUsingTest < ActiveSupport::TestCase
34
31
  end
35
32
 
36
33
  def test_even_explicitly_loading_test_data_will_truncate_and_then_load_fixtures
37
- TestData.load
38
- TestData.load_rails_fixtures(self)
34
+ TestData.uses_test_data
35
+ TestData.uses_rails_fixtures(self)
39
36
 
40
37
  assert_equal 2, Boop.count
41
38
  end
42
39
 
43
40
  def test_load_and_rollback_leaves_them_as_is
44
41
  boop = Boop.first
45
- original_created_at_time = boop.created_at
46
- a_year_ago = 1.year.ago
42
+ original_created_on = boop.created_at.to_date
43
+ a_year_ago = 1.year.ago.to_date
47
44
 
48
45
  boop.update!(created_at: a_year_ago)
49
46
 
50
- assert_equal Boop.find(boop.id).created_at, a_year_ago
47
+ assert_equal Boop.find(boop.id).created_at.to_date, a_year_ago
51
48
 
52
- # Now after rollback
53
- TestData.rollback(:after_load_rails_fixtures)
49
+ # Now trigger a rollback to the fixtures point
50
+ TestData.uses_rails_fixtures(self)
54
51
 
55
- assert_equal Boop.find(boop.id).created_at, original_created_at_time
56
- end
57
-
58
- teardown do
59
- TestData.rollback(:after_load_rails_fixtures)
52
+ assert_equal Boop.find(boop.id).created_at.to_date, original_created_on
60
53
  end
61
54
  end
62
55
 
@@ -65,35 +58,29 @@ class SomeFixturesAndSomeTestDataInOneClassTest < ActiveSupport::TestCase
65
58
  fixtures :all
66
59
 
67
60
  def test_fixtures_work
68
- TestData.load_rails_fixtures(self)
61
+ TestData.uses_rails_fixtures(self)
69
62
 
70
63
  assert_equal Date.civil(2020, 1, 1), boops(:boop_1).updated_at.to_date
71
64
  assert_equal "Levi", pants(:pant_1).brand
72
-
73
- TestData.rollback(:after_load_rails_fixtures)
74
65
  end
75
66
 
76
67
  def test_that_rewinds_to_test_data
77
- TestData.load
68
+ TestData.uses_test_data
78
69
 
79
70
  assert_equal 15, Boop.count
80
-
81
- TestData.rollback
82
71
  end
83
72
 
84
73
  def test_that_rewinds_to_the_very_start
85
- TestData.rollback(:before_data_load)
74
+ TestData.uninitialize
86
75
 
87
76
  assert_equal 0, Boop.count
88
77
  end
89
78
 
90
79
  def test_fixtures_get_reloaded_because_cache_is_cleared
91
- TestData.load_rails_fixtures(self)
80
+ TestData.uses_rails_fixtures(self)
92
81
 
93
82
  assert_equal Date.civil(2019, 1, 1), boops(:boop_2).updated_at.to_date
94
83
  assert_equal "Wrangler", pants(:pant_2).brand
95
-
96
- TestData.rollback(:after_load_rails_fixtures)
97
84
  end
98
85
  end
99
86
 
@@ -101,11 +88,7 @@ class PantsFixturesTest < ActiveSupport::TestCase
101
88
  fixtures :pants
102
89
 
103
90
  setup do
104
- TestData.load_rails_fixtures(self)
105
- end
106
-
107
- teardown do
108
- TestData.rollback(:after_load_rails_fixtures)
91
+ TestData.uses_rails_fixtures(self)
109
92
  end
110
93
 
111
94
  def test_has_fixture_pants
@@ -120,8 +103,8 @@ end
120
103
  class FixtureTestPassingTheWrongThingTest < ActiveSupport::TestCase
121
104
  def test_doing_it_wrong
122
105
  error = assert_raises(TestData::Error) do
123
- TestData.load_rails_fixtures(ActiveRecord::Base)
106
+ TestData.uses_rails_fixtures(ActiveRecord::Base)
124
107
  end
125
- assert_match "'TestData.load_rails_fixtures' must be passed a test instance that has had ActiveRecord::TestFixtures mixed-in (e.g. `TestData.load_rails_fixtures(self)` in an ActiveSupport::TestCase `setup` block), but the provided argument does not respond to 'setup_fixtures'", error.message
108
+ assert_match "'TestData.uses_rails_fixtures(self)' must be passed a test instance that has had ActiveRecord::TestFixtures mixed-in (e.g. `TestData.uses_rails_fixtures(self)` in an ActiveSupport::TestCase `setup` block), but the provided argument does not respond to 'setup_fixtures'", error.message
126
109
  end
127
110
  end
@@ -0,0 +1,89 @@
1
+ require "test_helper"
2
+
3
+ TestData.config do |config|
4
+ config.after_test_data_load { MetaBoop.refresh_materialized_view }
5
+ config.after_test_data_truncate(-> { MetaBoop.refresh_materialized_view })
6
+ config.after_rails_fixture_load { MetaBoop.refresh_materialized_view }
7
+ end
8
+
9
+ TestData.prevent_rails_fixtures_from_loading_automatically!
10
+ MetaBoop.refresh_materialized_view # count = 1
11
+
12
+ class TestDataHooksTest < ActiveSupport::TestCase
13
+ fixtures :all
14
+ i_suck_and_my_tests_are_order_dependent!
15
+
16
+ def test_uses_test_data_hook
17
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
18
+ MetaBoop.reset_refresh_materialized_view_count
19
+
20
+ # Materialized view is refreshed and called 1 time
21
+ TestData.uses_test_data
22
+ assert_equal 15, Boop.count
23
+ assert_equal 15, MetaBoop.count
24
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
25
+ MetaBoop.reset_refresh_materialized_view_count
26
+
27
+ # Rollbacks also rollback to materialized view changes without calling again
28
+ Boop.create!(other_boop: Boop.new)
29
+ assert_equal 16, Boop.count
30
+ assert_equal 15, MetaBoop.count
31
+ MetaBoop.refresh_materialized_view
32
+ assert_equal 16, MetaBoop.count
33
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
34
+ TestData.uses_test_data
35
+ assert_equal 15, Boop.count
36
+ assert_equal 15, MetaBoop.count
37
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
38
+ MetaBoop.reset_refresh_materialized_view_count
39
+
40
+ # The same hook also works when cleaning slates
41
+ TestData.uses_clean_slate
42
+ assert_equal 0, Boop.count
43
+ assert_equal 0, MetaBoop.count
44
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
45
+ Boop.create!(other_boop: Boop.new)
46
+ MetaBoop.refresh_materialized_view
47
+ assert_equal 1, Boop.count
48
+ assert_equal 1, MetaBoop.count
49
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
50
+ TestData.uses_clean_slate
51
+ assert_equal 0, Boop.count
52
+ assert_equal 0, MetaBoop.count
53
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
54
+ MetaBoop.reset_refresh_materialized_view_count
55
+
56
+ # The same hook works with fixtures
57
+ TestData.uses_rails_fixtures(self)
58
+ assert_equal 2, Boop.count
59
+ assert_equal 2, MetaBoop.count
60
+ assert_equal 1, MetaBoop.refresh_materialized_view_count
61
+ Boop.first.delete
62
+ assert_equal 1, Boop.count
63
+ assert_equal 2, MetaBoop.count
64
+ MetaBoop.refresh_materialized_view
65
+ assert_equal 1, MetaBoop.count
66
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
67
+ TestData.uses_rails_fixtures(self)
68
+ assert_equal 2, Boop.count
69
+ assert_equal 2, MetaBoop.count
70
+ assert_equal 2, MetaBoop.refresh_materialized_view_count
71
+ MetaBoop.reset_refresh_materialized_view_count
72
+
73
+ # Rewinding two steps will not call refresh materialized views
74
+ TestData.uses_test_data
75
+ assert_equal 15, Boop.count
76
+ assert_equal 15, MetaBoop.count
77
+ assert_equal 0, MetaBoop.refresh_materialized_view_count
78
+ end
79
+
80
+ def test_that_hooks_require_valid_settings
81
+ foo = Struct.new(:thing)
82
+ assert_raises(TestData::Error) { TestData.config.after_test_data_load(nil) }
83
+ assert_raises(TestData::Error) { TestData.config.after_test_data_truncate(nil) }
84
+ assert_raises(TestData::Error) { TestData.config.after_rails_fixture_load(nil) }
85
+ assert_raises(TestData::Error) { TestData.config.after_test_data_load(foo) }
86
+ assert_raises(TestData::Error) { TestData.config.after_test_data_truncate(foo) }
87
+ assert_raises(TestData::Error) { TestData.config.after_rails_fixture_load(foo) }
88
+ end
89
+ end
@@ -1,13 +1,11 @@
1
1
  require "test_helper"
2
2
 
3
- TestData.config.use_transactional_data_loader = false
4
-
5
3
  class TransactionCommittingTestCase < ActiveSupport::TestCase
6
4
  self.use_transactional_tests = false
7
5
 
8
6
  setup do
9
7
  Noncommittal.stop!
10
- TestData.load
8
+ TestData.insert_test_data_dump
11
9
  end
12
10
 
13
11
  teardown do
@@ -26,11 +24,4 @@ class TransactionCommittingBoopsTest < TransactionCommittingTestCase
26
24
  def test_finds_the_boops_via_another_process
27
25
  assert_equal 15, `RAILS_ENV=test bin/rails runner "puts Boop.count"`.chomp.to_i
28
26
  end
29
-
30
- def test_cant_have_it_both_ways
31
- error = assert_raise(TestData::Error) do
32
- TestData.config.use_transactional_data_loader = true
33
- end
34
- assert_match "There is already a non-transactional data loader", error.message
35
- end
36
27
  end
@@ -9,10 +9,6 @@ class SerializedNonTransactionalTestCase < ActiveSupport::TestCase
9
9
  self.use_transactional_tests = false
10
10
 
11
11
  setup do
12
- TestData.load
13
- end
14
-
15
- teardown do
16
- TestData.rollback
12
+ TestData.uses_test_data
17
13
  end
18
14
  end
@@ -9,6 +9,10 @@ module TestData
9
9
  require_relative "development"
10
10
 
11
11
  Rails.application.configure do
12
+ # Rails creates secret key base for only "development" and "test"
13
+ # For more info, see: https://github.com/testdouble/test_data/issues/2
14
+ self.secrets.secret_key_base ||= Rails.application.send(:generate_development_secret)
15
+
12
16
  # Don't persist schema.rb or structure.sql after test_data is migrated
13
17
  config.active_record.dump_schema_after_migration = false
14
18
  end
@@ -8,14 +8,17 @@ module TestData
8
8
  return unless defined?(TestData)
9
9
 
10
10
  TestData.config do |config|
11
- # Where to store SQL dumps of the test_data database schema
12
- # config.schema_dump_path = "test/support/test_data/schema.sql"
11
+ # Hook run after test data is loaded by `TestData.uses_test_data`,
12
+ # but before a savepoint is taken
13
+ # config.after_test_data_load { }
13
14
 
14
- # Where to store SQL dumps of the test_data database test data
15
- # config.data_dump_path = "test/support/test_data/data.sql"
15
+ # Hook run after test data is truncated by `TestData.uses_clean_slate`,
16
+ # but before a savepoint is taken
17
+ # config.after_test_data_truncate { }
16
18
 
17
- # Where to store SQL dumps of the test_data database non-test data
18
- # config.non_test_data_dump_path = "test/support/test_data/non_test_data.sql"
19
+ # Hook run after test data is truncated by `TestData.uses_rails_fixtures`,
20
+ # but before a savepoint is taken
21
+ # config.after_rails_fixture_load { }
19
22
 
20
23
  # Tables whose data shouldn't be loaded into tests.
21
24
  # ("ar_internal_metadata" and "schema_migrations" are always excluded)
@@ -24,17 +27,20 @@ module TestData
24
27
  # Tables whose data should be excluded from SQL dumps (still dumps their schema DDL)
25
28
  # config.dont_dump_these_tables = []
26
29
 
27
- # Tables whose data should be truncated by TestData.truncate
30
+ # Where to store SQL dumps of the test_data database schema
31
+ # config.schema_dump_path = "test/support/test_data/schema.sql"
32
+
33
+ # Where to store SQL dumps of the test_data database test data
34
+ # config.data_dump_path = "test/support/test_data/data.sql"
35
+
36
+ # Where to store SQL dumps of the test_data database non-test data
37
+ # config.non_test_data_dump_path = "test/support/test_data/non_test_data.sql"
38
+
39
+ # Tables whose data should be truncated by TestData.uses_clean_slate
28
40
  # If left as `nil`, all tables inserted into by the SQL file at
29
41
  # `data_dump_path` will be truncated
30
42
  # config.truncate_these_test_data_tables = nil
31
43
 
32
- # Perform TestData.load and TestData.truncate inside nested
33
- # transactions for increased test isolation and speed. Setting this
34
- # to false will disable several features that depend on transactions
35
- # being used
36
- # config.use_transactional_data_loader = true
37
-
38
44
  # Log level (valid values: [:debug, :info, :warn, :error, :quiet])
39
45
  # Can also be set with env var TEST_DATA_LOG_LEVEL
40
46
  # config.log_level = :info
@@ -29,18 +29,9 @@ module TestData
29
29
  # Tables to exclude from all dumps
30
30
  attr_accessor :dont_dump_these_tables
31
31
 
32
- # Tables to truncate when TestData.truncate is called
32
+ # Tables to truncate when TestData.uses_clean_slate is called
33
33
  attr_accessor :truncate_these_test_data_tables
34
34
 
35
- # Perform TestData.load and TestData.truncate inside nested
36
- # transactions for increased test isolation and speed. Setting this to false
37
- # will disable several features that depend on transactions being used
38
- attr_reader :use_transactional_data_loader
39
- def use_transactional_data_loader=(use_transactions)
40
- TestData.ensure_we_dont_mix_transactional_and_non_transactional_data_loaders!(use_transactions)
41
- @use_transactional_data_loader = use_transactions
42
- end
43
-
44
35
  # Log level (valid values: [:debug, :info, :warn, :error, :quiet])
45
36
  def log_level
46
37
  TestData.log.level
@@ -50,7 +41,8 @@ module TestData
50
41
  TestData.log.level = level
51
42
  end
52
43
 
53
- attr_reader :pwd, :cable_yaml_path, :database_yaml_path, :secrets_yaml_path
44
+ attr_reader :pwd, :cable_yaml_path, :database_yaml_path, :secrets_yaml_path,
45
+ :after_test_data_load_hook, :after_test_data_truncate_hook, :after_rails_fixture_load_hook
54
46
 
55
47
  def self.full_path_reader(*relative_path_readers)
56
48
  relative_path_readers.each do |relative_path_reader|
@@ -73,7 +65,33 @@ module TestData
73
65
  @non_test_data_tables = []
74
66
  @dont_dump_these_tables = []
75
67
  @truncate_these_test_data_tables = nil
76
- @use_transactional_data_loader = true
68
+ @after_test_data_load_hook = -> {}
69
+ @after_test_data_truncate_hook = -> {}
70
+ @after_rails_fixture_load_hook = -> {}
71
+ end
72
+
73
+ def after_test_data_load(callable = nil, &blk)
74
+ hook = callable || blk
75
+ if !hook.respond_to?(:call)
76
+ raise Error.new("after_test_data_load must be passed a callable (e.g. a Proc) or called with a block")
77
+ end
78
+ @after_test_data_load_hook = hook
79
+ end
80
+
81
+ def after_test_data_truncate(callable = nil, &blk)
82
+ hook = callable || blk
83
+ if !hook.respond_to?(:call)
84
+ raise Error.new("after_test_data_truncate must be passed a callable (e.g. a Proc) or called with a block")
85
+ end
86
+ @after_test_data_truncate_hook = hook
87
+ end
88
+
89
+ def after_rails_fixture_load(callable = nil, &blk)
90
+ hook = callable || blk
91
+ if !hook.respond_to?(:call)
92
+ raise Error.new("after_rails_fixture_load must be passed a callable (e.g. a Proc) or called with a block")
93
+ end
94
+ @after_rails_fixture_load_hook = hook
77
95
  end
78
96
 
79
97
  def database_yaml