with_model 2.3.1 → 2.5.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.
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ # A `table(false)` model that is its own base class and names an existing table
6
+ # rather than inheriting one. `with_table` is declared first, so the table is
7
+ # created before the model and dropped after it.
8
+ class OwnTableNameTest < Minitest::Test
9
+ with_table :mythical_beasts do |t|
10
+ t.string "name"
11
+ end
12
+
13
+ with_model :Hippogriff do
14
+ table(false)
15
+
16
+ model do
17
+ self.table_name = "mythical_beasts"
18
+ end
19
+ end
20
+
21
+ def test_the_model_uses_the_table_it_names
22
+ Hippogriff.create!(name: "Buckbeak")
23
+
24
+ assert_equal "mythical_beasts", Hippogriff.table_name
25
+ assert_equal 1, Hippogriff.count
26
+ end
27
+
28
+ def test_the_model_creates_no_table_of_its_own
29
+ assert_empty ActiveRecord::Base.connection.tables.grep(/with_model_hippogriff/)
30
+ end
31
+ end
32
+
33
+ # A `table(false)` model that is its own base class and uses the table Active
34
+ # Record names after it, without naming it in the model block.
35
+ class ConventionalTableNameTest < Minitest::Test
36
+ with_table :unicorns do |t|
37
+ t.string "name"
38
+ end
39
+
40
+ with_model :Unicorn do
41
+ table(false)
42
+ end
43
+
44
+ def test_the_model_uses_the_table_named_after_it
45
+ Unicorn.create!(name: "Charlie")
46
+
47
+ assert_equal "unicorns", Unicorn.table_name
48
+ assert_equal 1, Unicorn.count
49
+ end
50
+ end
51
+
52
+ # A `table(false)` model whose superclass is abstract and, needing no table, is
53
+ # declared with `table(false)` too.
54
+ class AbstractSuperclassTableNameTest < Minitest::Test
55
+ with_table :winged_horses do |t|
56
+ t.string "name"
57
+ end
58
+
59
+ with_model :FlyingCreature do
60
+ table(false)
61
+
62
+ model do
63
+ self.abstract_class = true
64
+ end
65
+ end
66
+
67
+ with_model :Pegasus, superclass: :FlyingCreature do
68
+ table(false)
69
+
70
+ model do
71
+ self.table_name = "winged_horses"
72
+ end
73
+ end
74
+
75
+ def test_the_model_uses_the_table_it_names
76
+ Pegasus.create!(name: "Bellerophon")
77
+
78
+ assert_equal "winged_horses", Pegasus.table_name
79
+ assert_equal 1, Pegasus.count
80
+ end
81
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ class TestUnitLifecycleBaseTest < Test::Unit::TestCase
6
+ FIXTURE_EVENTS = []
7
+
8
+ with_model :TestUnitLifecycleParent do
9
+ table { |t| t.string "name" }
10
+ end
11
+
12
+ def setup
13
+ FIXTURE_EVENTS.clear
14
+ assert_kind_of Class, TestUnitLifecycleParent
15
+ FIXTURE_EVENTS << :inherited_setup
16
+ end
17
+
18
+ def teardown
19
+ assert_kind_of Class, TestUnitLifecycleParent
20
+ FIXTURE_EVENTS << :inherited_teardown
21
+ end
22
+ end
23
+
24
+ class TestUnitLifecycleTest < TestUnitLifecycleBaseTest
25
+ with_model :TestUnitLifecycleChild do
26
+ table do |t|
27
+ t.references "parent", foreign_key: {to_table: TestUnitLifecycleParent.table_name}
28
+ end
29
+
30
+ model { belongs_to :parent, class_name: TestUnitLifecycleParent.name }
31
+ end
32
+
33
+ def setup
34
+ super
35
+ assert_kind_of Class, TestUnitLifecycleChild
36
+ FIXTURE_EVENTS << :local_setup
37
+ end
38
+
39
+ def teardown
40
+ assert_kind_of Class, TestUnitLifecycleChild
41
+ FIXTURE_EVENTS << :local_teardown
42
+ super
43
+ return unless name == "test_models_are_available_through_user_fixtures"
44
+
45
+ assert_equal [
46
+ :inherited_setup,
47
+ :local_setup,
48
+ :test_body,
49
+ :local_teardown,
50
+ :inherited_teardown
51
+ ], FIXTURE_EVENTS
52
+ end
53
+
54
+ def test_models_are_available_through_user_fixtures
55
+ parent = TestUnitLifecycleParent.create!(name: "parent")
56
+ child = TestUnitLifecycleChild.create!(parent: parent)
57
+
58
+ assert_equal parent, child.reload.parent
59
+ FIXTURE_EVENTS << :test_body
60
+ end
61
+
62
+ def test_model_destruction_unwinds_inheritance_order
63
+ parent_test_case = Class.new(Test::Unit::TestCase) do
64
+ with_model :TestUnitLifecycleOrderParent do
65
+ table
66
+ end
67
+ end
68
+ test_case = Class.new(parent_test_case) do
69
+ teardown(after: :prepend) do
70
+ assert_false Object.const_defined?(:TestUnitLifecycleOrderChild)
71
+ assert_false ActiveRecord::Base.connection.data_source_exists?(child_table_name)
72
+ assert_kind_of Class, TestUnitLifecycleOrderParent
73
+ assert ActiveRecord::Base.connection.data_source_exists?(parent_table_name)
74
+ end
75
+
76
+ with_model :TestUnitLifecycleOrderChild do
77
+ table
78
+ end
79
+
80
+ define_method(:parent_table_name) { TestUnitLifecycleOrderParent.table_name }
81
+ define_method(:child_table_name) { @child_table_name }
82
+ define_method(:test_lifecycle) { @child_table_name = TestUnitLifecycleOrderChild.table_name }
83
+ end
84
+
85
+ result = Test::Unit::TestResult.new
86
+ test_case.new("test_lifecycle").run(result) {}
87
+
88
+ assert_equal 1, result.run_count
89
+ assert_equal 0, result.error_count
90
+ end
91
+ end
92
+
93
+ class TestUnitUserHookFailureTest < Test::Unit::TestCase
94
+ def test_setup_error_does_not_prevent_model_cleanup
95
+ test_case = Class.new(Test::Unit::TestCase) do
96
+ class << self
97
+ attr_accessor :created_table_name
98
+ end
99
+
100
+ with_model :TestUnitSetupFailureModel do
101
+ table
102
+ end
103
+
104
+ def setup
105
+ self.class.created_table_name = TestUnitSetupFailureModel.table_name
106
+ raise "setup failed"
107
+ end
108
+
109
+ def test_setup_failure
110
+ end
111
+ end
112
+
113
+ result = Test::Unit::TestResult.new
114
+ test_case.new("test_setup_failure").run(result) {}
115
+
116
+ assert_hook_failure_cleans_up_model(
117
+ result,
118
+ RuntimeError,
119
+ "setup failed",
120
+ :TestUnitSetupFailureModel,
121
+ test_case.created_table_name
122
+ )
123
+ end
124
+
125
+ def test_teardown_error_does_not_prevent_model_cleanup
126
+ test_case = Class.new(Test::Unit::TestCase) do
127
+ class << self
128
+ attr_accessor :created_table_name
129
+ end
130
+
131
+ with_model :TestUnitTeardownFailureModel do
132
+ table
133
+ end
134
+
135
+ def teardown
136
+ self.class.created_table_name = TestUnitTeardownFailureModel.table_name
137
+ raise "teardown failed"
138
+ end
139
+
140
+ def test_teardown_failure
141
+ end
142
+ end
143
+
144
+ result = Test::Unit::TestResult.new
145
+ test_case.new("test_teardown_failure").run(result) {}
146
+
147
+ assert_hook_failure_cleans_up_model(
148
+ result,
149
+ RuntimeError,
150
+ "teardown failed",
151
+ :TestUnitTeardownFailureModel,
152
+ test_case.created_table_name
153
+ )
154
+ end
155
+
156
+ private
157
+
158
+ def assert_hook_failure_cleans_up_model(result, error_class, message, constant, table_name)
159
+ assert_equal 1, result.run_count
160
+ assert_equal 1, result.error_count
161
+ assert_equal error_class, result.errors.first.exception.class
162
+ assert_equal message, result.errors.first.exception.message
163
+ assert_false Object.const_defined?(constant)
164
+ assert_false ActiveRecord::Base.connection.data_source_exists?(table_name)
165
+ end
166
+ end
167
+
168
+ class TestUnitPartialModelCreationFailureTest < Test::Unit::TestCase
169
+ def test_preserves_the_setup_error_when_model_creation_does_not_assign_a_model
170
+ test_case = Class.new(Test::Unit::TestCase) do
171
+ with_model :TestUnitPartiallyCreatedModel, superclass: Object do
172
+ table(false)
173
+ end
174
+
175
+ def test_setup_failure
176
+ end
177
+ end
178
+
179
+ result = Test::Unit::TestResult.new
180
+ test_case.new("test_setup_failure").run(result) {}
181
+
182
+ assert_equal 1, result.run_count
183
+ assert_equal 1, result.error_count
184
+ assert_kind_of WithModel::InvalidSuperclass, result.errors.first.exception
185
+ end
186
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ # A `table(false)` model that is its own base class and names an existing table
6
+ # rather than inheriting one. `with_table` is declared first, so the table is
7
+ # created before the model and dropped after it.
8
+ class TestUnitOwnTableNameTest < Test::Unit::TestCase
9
+ with_table :test_unit_mythical_beasts do |t|
10
+ t.string "name"
11
+ end
12
+
13
+ with_model :TestUnitHippogriff do
14
+ table(false)
15
+
16
+ model do
17
+ self.table_name = "test_unit_mythical_beasts"
18
+ end
19
+ end
20
+
21
+ def test_the_model_uses_the_table_it_names
22
+ TestUnitHippogriff.create!(name: "Buckbeak")
23
+
24
+ assert_equal "test_unit_mythical_beasts", TestUnitHippogriff.table_name
25
+ assert_equal 1, TestUnitHippogriff.count
26
+ end
27
+
28
+ def test_the_model_creates_no_table_of_its_own
29
+ assert_empty ActiveRecord::Base.connection.tables.grep(/with_model_test_unit_hippogriff/)
30
+ end
31
+ end
32
+
33
+ # A `table(false)` model that is its own base class and uses the table Active
34
+ # Record names after it, without naming it in the model block.
35
+ class TestUnitConventionalTableNameTest < Test::Unit::TestCase
36
+ with_table :test_unit_unicorns do |t|
37
+ t.string "name"
38
+ end
39
+
40
+ with_model :TestUnitUnicorn do
41
+ table(false)
42
+ end
43
+
44
+ def test_the_model_uses_the_table_named_after_it
45
+ TestUnitUnicorn.create!(name: "Charlie")
46
+
47
+ assert_equal "test_unit_unicorns", TestUnitUnicorn.table_name
48
+ assert_equal 1, TestUnitUnicorn.count
49
+ end
50
+ end
51
+
52
+ # A `table(false)` model whose superclass is abstract and, needing no table, is
53
+ # declared with `table(false)` too.
54
+ class TestUnitAbstractSuperclassTableNameTest < Test::Unit::TestCase
55
+ with_table :test_unit_winged_horses do |t|
56
+ t.string "name"
57
+ end
58
+
59
+ with_model :TestUnitFlyingCreature do
60
+ table(false)
61
+
62
+ model do
63
+ self.abstract_class = true
64
+ end
65
+ end
66
+
67
+ with_model :TestUnitPegasus, superclass: :TestUnitFlyingCreature do
68
+ table(false)
69
+
70
+ model do
71
+ self.table_name = "test_unit_winged_horses"
72
+ end
73
+ end
74
+
75
+ def test_the_model_uses_the_table_it_names
76
+ TestUnitPegasus.create!(name: "Bellerophon")
77
+
78
+ assert_equal "test_unit_winged_horses", TestUnitPegasus.table_name
79
+ assert_equal 1, TestUnitPegasus.count
80
+ end
81
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ # Stands in for an application's own model, the usual reason to want single table
6
+ # inheritance: its table is created once and outlives every test here, so a
7
+ # with_model child's rows cannot be disposed of by dropping the table.
8
+ module TestUnitAppModels
9
+ class Cupboard < ActiveRecord::Base
10
+ end
11
+ end
12
+
13
+ ActiveRecord::Base.connection.create_table TestUnitAppModels::Cupboard.table_name,
14
+ force: true do |t|
15
+ t.string "type"
16
+ t.string "name"
17
+ end
18
+
19
+ # A with_model parent and an STI child in the same test case. The child's
20
+ # superclass is resolved per-example, so this only works if the parent is
21
+ # created before the child and destroyed after it.
22
+ class TestUnitStiTest < Test::Unit::TestCase
23
+ with_model :TestUnitVehicle do
24
+ table do |t|
25
+ t.string "type"
26
+ t.string "name"
27
+ end
28
+ end
29
+
30
+ with_model :TestUnitTruck, superclass: -> { TestUnitVehicle } do
31
+ table(false)
32
+ end
33
+
34
+ with_model :TestUnitChest, superclass: "TestUnitAppModels::Cupboard" do
35
+ table(false)
36
+ end
37
+
38
+ def test_the_child_shares_the_parents_table
39
+ assert_equal TestUnitVehicle.table_name, TestUnitTruck.table_name
40
+ end
41
+
42
+ def test_the_child_stores_its_own_type
43
+ truck = TestUnitTruck.create!(name: "Ford F-150")
44
+
45
+ assert_equal "TestUnitTruck", truck.reload.type
46
+ assert_instance_of TestUnitTruck, TestUnitVehicle.first
47
+ end
48
+
49
+ # Rows in a table the child does not own have to be deleted when it goes away,
50
+ # since nothing else will: Cupboard's table is still standing afterwards, and a
51
+ # row naming a class that no longer exists makes it unloadable. Both tests write
52
+ # one and first insist the table is empty, so whichever test happens to run
53
+ # second fails if the rows survived teardown.
54
+ def test_the_childs_rows_do_not_outlive_it
55
+ assert_empty TestUnitAppModels::Cupboard.all,
56
+ "a previous test's rows outlived it"
57
+
58
+ TestUnitChest.create!(name: "sideboard")
59
+
60
+ assert_equal 1, TestUnitAppModels::Cupboard.count
61
+ end
62
+
63
+ def test_the_childs_rows_leave_the_superclass_loadable
64
+ assert_empty TestUnitAppModels::Cupboard.all,
65
+ "a previous test's rows outlived it"
66
+
67
+ TestUnitChest.create!(name: "wardrobe")
68
+
69
+ assert_instance_of TestUnitChest, TestUnitAppModels::Cupboard.first
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+
5
+ require "active_record"
6
+ require "test/unit"
7
+ require "with_model"
8
+
9
+ WithModel.runner = :test_unit
10
+
11
+ Test::Unit::TestCase.extend WithModel
12
+
13
+ # WithModel requires ActiveRecord::Base.connection to be established.
14
+ # If ActiveRecord already has a connection, as in a Rails app, this is unnecessary.
15
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ class TestUnitWithModelTest < Test::Unit::TestCase
6
+ with_model :TestUnitBlogPost do
7
+ table { |t| t.string "title" }
8
+ model { define_method(:fancy_title) { "Title: #{title}" } }
9
+ end
10
+
11
+ with_table :test_unit_widgets do |t|
12
+ t.string "name"
13
+ end
14
+
15
+ def test_creates_a_temporary_active_record_model
16
+ record = TestUnitBlogPost.create!(title: "New blog post")
17
+
18
+ assert_equal "New blog post", record.reload.title
19
+ end
20
+
21
+ def test_defines_model_methods
22
+ assert_equal "Title: New blog post", TestUnitBlogPost.new(title: "New blog post").fancy_title
23
+ end
24
+
25
+ def test_defines_the_model_constant
26
+ assert_kind_of Class, TestUnitBlogPost
27
+ end
28
+
29
+ def test_creates_a_temporary_table
30
+ assert_true ActiveRecord::Base.connection.data_source_exists?("test_unit_widgets")
31
+ end
32
+ end
33
+
34
+ class TestUnitPerDeclarationRunnerTest < Test::Unit::TestCase
35
+ with_model :TestUnitPerDeclarationModel, runner: :test_unit do
36
+ table
37
+ end
38
+
39
+ def test_supports_a_test_unit_runner_override
40
+ assert_kind_of Class, TestUnitPerDeclarationModel
41
+ end
42
+ end
43
+
44
+ class TestUnitRunnerValidationTest < Test::Unit::TestCase
45
+ def test_names_all_supported_runners_for_an_unsupported_runner
46
+ error = assert_raise(ArgumentError) do
47
+ Class.new(Test::Unit::TestCase) do
48
+ extend WithModel
49
+
50
+ with_model :UnsupportedTestUnitModel, runner: :unsupported do
51
+ table
52
+ end
53
+ end
54
+ end
55
+
56
+ assert_equal "Unsupported test runner set, expected :rspec, :minitest, or :test_unit", error.message
57
+ end
58
+ end
data/with_model.gemspec CHANGED
@@ -9,7 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Case Commons, LLC", "Grant Hutchins", "Andrew Marshall"]
10
10
  spec.email = %w[casecommons-dev@googlegroups.com gems@nertzy.com andrew@johnandrewmarshall.com]
11
11
  spec.homepage = "https://github.com/Casecommons/with_model"
12
- spec.summary = "Dynamically build a model within an RSpec context"
12
+ spec.summary = "Dynamically build an Active Record model for " \
13
+ "RSpec, Minitest, and Test::Unit tests"
13
14
  spec.description = spec.summary
14
15
  spec.license = "MIT"
15
16
  spec.metadata["rubygems_mfa_required"] = "true"
@@ -20,5 +21,5 @@ Gem::Specification.new do |spec|
20
21
 
21
22
  spec.required_ruby_version = ">= 3.3"
22
23
 
23
- spec.add_dependency "activerecord", ">= 7.2"
24
+ spec.add_dependency "activerecord", ">= 8.0"
24
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Case Commons, LLC
@@ -17,15 +17,16 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '7.2'
20
+ version: '8.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '7.2'
28
- description: Dynamically build a model within an RSpec context
27
+ version: '8.0'
28
+ description: Dynamically build an Active Record model for RSpec, Minitest, and Test::Unit
29
+ tests
29
30
  email:
30
31
  - casecommons-dev@googlegroups.com
31
32
  - gems@nertzy.com
@@ -47,29 +48,43 @@ files:
47
48
  - LICENSE
48
49
  - README.md
49
50
  - Rakefile
51
+ - docs/minitest.md
52
+ - docs/rspec.md
53
+ - docs/test-unit.md
50
54
  - lib/with_model.rb
51
55
  - lib/with_model/constant_stubber.rb
52
56
  - lib/with_model/descendants_tracker.rb
53
57
  - lib/with_model/invalid_superclass.rb
54
58
  - lib/with_model/methods.rb
59
+ - lib/with_model/minitest.rb
55
60
  - lib/with_model/missing_superclass.rb
56
61
  - lib/with_model/model.rb
57
62
  - lib/with_model/model/dsl.rb
58
63
  - lib/with_model/null_table.rb
64
+ - lib/with_model/rspec.rb
59
65
  - lib/with_model/table.rb
66
+ - lib/with_model/test_unit.rb
60
67
  - lib/with_model/version.rb
61
68
  - spec/active_record_behaviors_spec.rb
62
69
  - spec/constant_stubber_spec.rb
63
70
  - spec/descendants_tracking_spec.rb
64
71
  - spec/readme_spec.rb
72
+ - spec/runner_entrypoints_spec.rb
65
73
  - spec/spec_helper.rb
66
74
  - spec/table_false_spec.rb
75
+ - spec/table_spec.rb
67
76
  - spec/with_model_spec.rb
68
77
  - test/declaration_order_test.rb
78
+ - test/own_table_name_test.rb
69
79
  - test/spec_dsl_test.rb
70
80
  - test/sti_test.rb
71
81
  - test/test_helper.rb
72
82
  - test/with_model_test.rb
83
+ - test_unit/lifecycle_test.rb
84
+ - test_unit/own_table_name_test.rb
85
+ - test_unit/sti_test.rb
86
+ - test_unit/test_unit_helper.rb
87
+ - test_unit/with_model_test.rb
73
88
  - with_model.gemspec
74
89
  homepage: https://github.com/Casecommons/with_model
75
90
  licenses:
@@ -90,7 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
105
  - !ruby/object:Gem::Version
91
106
  version: '0'
92
107
  requirements: []
93
- rubygems_version: 4.0.18
108
+ rubygems_version: 4.0.20
94
109
  specification_version: 4
95
- summary: Dynamically build a model within an RSpec context
110
+ summary: Dynamically build an Active Record model for RSpec, Minitest, and Test::Unit
111
+ tests
96
112
  test_files: []