test_data 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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