with_model 2.3.1 → 2.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -12
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +20 -3
- data/Gemfile +1 -0
- data/README.md +47 -10
- data/Rakefile +7 -1
- data/docs/minitest.md +45 -0
- data/docs/rspec.md +43 -0
- data/docs/test-unit.md +73 -0
- data/lib/with_model/descendants_tracker.rb +3 -11
- data/lib/with_model/minitest.rb +7 -0
- data/lib/with_model/model.rb +0 -13
- data/lib/with_model/rspec.rb +10 -0
- data/lib/with_model/table.rb +2 -8
- data/lib/with_model/test_unit.rb +7 -0
- data/lib/with_model/version.rb +1 -1
- data/lib/with_model.rb +6 -1
- data/spec/descendants_tracking_spec.rb +20 -2
- data/spec/runner_entrypoints_spec.rb +153 -0
- data/spec/table_false_spec.rb +0 -4
- data/spec/table_spec.rb +37 -0
- data/test_unit/lifecycle_test.rb +186 -0
- data/test_unit/sti_test.rb +71 -0
- data/test_unit/test_unit_helper.rb +15 -0
- data/test_unit/with_model_test.rb +58 -0
- data/with_model.gemspec +3 -2
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f788d32a4cd252a5254b82928cab9b62b66cfd80e00dd010bdb901b60a6ddc6c
|
|
4
|
+
data.tar.gz: 2c44d64d8f3225e809c113894ec77df2e64c6dae513a12065e171bd892c80c7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbf514f03d362582bf15cd38301a9f2929c559ea702c283bae2c01720f48e3f613e93ce1580906983d567048d7241d70af92b94f54f4d992da06f3497c9afc8e
|
|
7
|
+
data.tar.gz: 66200b79760795f6d3e622b4b29c13285fb042e4e168f1fea34ac38161bca41e4330a451add6f9a0db777dd20cbde97567cd687c811dff1675a1090a24dc7704
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -29,8 +29,6 @@ jobs:
|
|
|
29
29
|
matrix:
|
|
30
30
|
ruby-version: ["3.3", "3.4", "4.0"]
|
|
31
31
|
active-record:
|
|
32
|
-
- label: AR 7.2
|
|
33
|
-
env: ACTIVE_RECORD_VERSION=~> 7.2.0
|
|
34
32
|
- label: AR 8.0
|
|
35
33
|
env: ACTIVE_RECORD_VERSION=~> 8.0.0
|
|
36
34
|
- label: AR 8.1
|
|
@@ -52,11 +50,6 @@ jobs:
|
|
|
52
50
|
label: AR 8-0-stable
|
|
53
51
|
env: ACTIVE_RECORD_BRANCH=8-0-stable
|
|
54
52
|
allow-failure: true
|
|
55
|
-
- ruby-version: "4.0"
|
|
56
|
-
active-record:
|
|
57
|
-
label: AR 7-2-stable
|
|
58
|
-
env: ACTIVE_RECORD_BRANCH=7-2-stable
|
|
59
|
-
allow-failure: true
|
|
60
53
|
# A canary for the next Ruby, which cannot fail the build.
|
|
61
54
|
- ruby-version: ruby-head
|
|
62
55
|
active-record:
|
|
@@ -78,11 +71,6 @@ jobs:
|
|
|
78
71
|
ruby-version: ${{ matrix.ruby-version }}
|
|
79
72
|
bundler-cache: true
|
|
80
73
|
cache-version: ${{ matrix.active-record.env }}
|
|
81
|
-
# Gemfile.lock is not checked in, so this resolves the newest gems that
|
|
82
|
-
# satisfy the Gemfile on each run rather than a recorded set. `--all` is
|
|
83
|
-
# what Bundler 4, shipped with Ruby 4.0, wants for that.
|
|
84
|
-
- name: Update bundle
|
|
85
|
-
run: bundle update --all
|
|
86
74
|
# Not bin/rake: bin/ holds generated binstubs and is not checked in, and
|
|
87
75
|
# whether bundling writes them there varies by Bundler version - Bundler 4
|
|
88
76
|
# does not, which left Ruby 4.0 with no binstub to run.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
### Unreleased
|
|
2
2
|
|
|
3
|
+
### 2.4.0
|
|
4
|
+
|
|
5
|
+
- Add Test::Unit support, including lifecycle integration for dynamic models and
|
|
6
|
+
tables.
|
|
7
|
+
- Add `with_model/rspec`, `with_model/minitest`, and `with_model/test_unit`
|
|
8
|
+
convenience entrypoints.
|
|
9
|
+
- Add runner integration guides for RSpec, Minitest, and Test::Unit.
|
|
10
|
+
- Skip temporary-table teardown when the table has already disappeared.
|
|
11
|
+
- Require Active Record 8.0 or later.
|
|
12
|
+
|
|
3
13
|
### 2.3.1
|
|
4
14
|
|
|
5
15
|
- Honor Active Record destruction callbacks and dependent associations when
|
data/CONTRIBUTING.md
CHANGED
|
@@ -24,12 +24,22 @@ avoid anything that only works on the newest of either.
|
|
|
24
24
|
|
|
25
25
|
## Running the tests
|
|
26
26
|
|
|
27
|
-
The suite runs against
|
|
28
|
-
under each:
|
|
27
|
+
The suite runs against all three supported test harnesses, so `with_model` has
|
|
28
|
+
to work under each:
|
|
29
29
|
|
|
30
30
|
- `spec/` covers behavior under RSpec.
|
|
31
|
-
- `test/` covers the
|
|
31
|
+
- `test/` covers the Minitest life cycle — the setup and teardown hooks, and
|
|
32
32
|
their ordering — rather than repeating the specs.
|
|
33
|
+
- `test_unit/` covers Test::Unit integration in a dedicated process, including
|
|
34
|
+
native fixture callback ordering.
|
|
35
|
+
|
|
36
|
+
Run one harness with its scoped Rake task:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
bundle exec rake spec
|
|
40
|
+
bundle exec rake test
|
|
41
|
+
bundle exec rake test_unit
|
|
42
|
+
```
|
|
33
43
|
|
|
34
44
|
Run everything, including the linter, with:
|
|
35
45
|
|
|
@@ -37,6 +47,13 @@ Run everything, including the linter, with:
|
|
|
37
47
|
bundle exec rake
|
|
38
48
|
```
|
|
39
49
|
|
|
50
|
+
Validate the README and runner guides with RDoc before submitting documentation
|
|
51
|
+
changes:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
rm -rf tmp/rdoc && bundle exec rdoc --markup markdown --op tmp/rdoc README.md docs/rspec.md docs/minitest.md docs/test-unit.md lib/**/*.rb
|
|
55
|
+
```
|
|
56
|
+
|
|
40
57
|
`bin/` holds generated binstubs and is not checked in. Whether bundling writes
|
|
41
58
|
them for you depends on your Bundler version, so create them once if you would
|
|
42
59
|
rather type `bin/rake`:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -14,32 +14,63 @@
|
|
|
14
14
|
|
|
15
15
|
Install as usual: `gem install with_model` or add `gem 'with_model'` to your Gemfile. See [`.github/workflows/ci.yml`](./.github/workflows/ci.yml) for supported (tested) Ruby versions.
|
|
16
16
|
|
|
17
|
+
## Test runner setup
|
|
18
|
+
|
|
19
|
+
Choose the entrypoint for the test runner that owns the declarations. Each
|
|
20
|
+
performs complete global setup for one runner; see its guide for lifecycle and
|
|
21
|
+
manual-setup details.
|
|
22
|
+
|
|
17
23
|
### RSpec
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
```ruby
|
|
26
|
+
require "with_model/rspec"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
See the [RSpec guide](docs/rspec.md).
|
|
30
|
+
|
|
31
|
+
### Minitest
|
|
20
32
|
|
|
21
33
|
```ruby
|
|
22
|
-
require "with_model"
|
|
34
|
+
require "with_model/minitest"
|
|
35
|
+
```
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
See the [Minitest guide](docs/minitest.md).
|
|
38
|
+
|
|
39
|
+
### Test::Unit
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
require "with_model/test_unit"
|
|
27
43
|
```
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
See the [Test::Unit guide](docs/test-unit.md).
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
### Advanced and mixed-runner setup
|
|
48
|
+
|
|
49
|
+
Plain `require "with_model"` remains framework-neutral. For manual setup,
|
|
50
|
+
select a runner and extend its test context directly:
|
|
32
51
|
|
|
33
52
|
```ruby
|
|
53
|
+
require "minitest"
|
|
34
54
|
require "with_model"
|
|
35
55
|
|
|
36
56
|
WithModel.runner = :minitest
|
|
57
|
+
Minitest::Test.extend WithModel
|
|
58
|
+
```
|
|
37
59
|
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
RSpec uses `RSpec.configure { |config| config.extend WithModel }`; Test::Unit
|
|
61
|
+
uses `Test::Unit::TestCase.extend WithModel`. A declaration can override the
|
|
62
|
+
global runner when needed:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
with_model :Post, runner: :test_unit do
|
|
66
|
+
table
|
|
40
67
|
end
|
|
41
68
|
```
|
|
42
69
|
|
|
70
|
+
Loading multiple convenience entrypoints in one process is unsupported because
|
|
71
|
+
the last one changes the global runner. For mixed runners, use plain
|
|
72
|
+
`with_model`, direct extension, and explicit `runner:` options.
|
|
73
|
+
|
|
43
74
|
## Usage
|
|
44
75
|
|
|
45
76
|
After setting up as above, call `with_model` and inside its block pass it a `table` block and a `model` block.
|
|
@@ -252,7 +283,13 @@ the order they are declared, and destroyed in the reverse order.
|
|
|
252
283
|
|
|
253
284
|
## Requirements
|
|
254
285
|
|
|
255
|
-
See the [gemspec metadata](https://rubygems.org/gems/with_model) for
|
|
286
|
+
See the [gemspec metadata](https://rubygems.org/gems/with_model) for Ruby and
|
|
287
|
+
Active Record requirements. RSpec, Minitest, and Test::Unit are optional
|
|
288
|
+
consumer framework dependencies; install the framework used by its selected
|
|
289
|
+
entrypoint. Requiring a selected entrypoint without its framework installed
|
|
290
|
+
raises the framework's ordinary `LoadError`.
|
|
291
|
+
|
|
292
|
+
`scope:` is RSpec-only and is silently ignored by Minitest and Test::Unit.
|
|
256
293
|
|
|
257
294
|
## Thread-safety
|
|
258
295
|
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,13 @@ RSpec::Core::RakeTask.new
|
|
|
8
8
|
require "minitest/test_task"
|
|
9
9
|
Minitest::TestTask.create
|
|
10
10
|
|
|
11
|
+
require "rake/testtask"
|
|
12
|
+
Rake::TestTask.new(:test_unit) do |task|
|
|
13
|
+
task.libs << "lib" << "test_unit" << "test"
|
|
14
|
+
task.pattern = "test_unit/**/*_test.rb"
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# standard rake task
|
|
12
18
|
require "standard/rake"
|
|
13
19
|
|
|
14
|
-
task default: %i[spec test standard]
|
|
20
|
+
task default: %i[spec test test_unit standard]
|
data/docs/minitest.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Minitest integration
|
|
2
|
+
|
|
3
|
+
Load the Minitest entrypoint before defining tests:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/minitest"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This requires `minitest`, then `with_model`, selects the
|
|
10
|
+
`:minitest` runner, and extends `Minitest::Test`. Its class-level DSL is
|
|
11
|
+
inherited by `Minitest::Test` subclasses, including `Minitest::Spec` and
|
|
12
|
+
`ActiveSupport::TestCase`. The entrypoint does not require or start Minitest's
|
|
13
|
+
autorun — your process or test framework owns Minitest startup.
|
|
14
|
+
|
|
15
|
+
## Lifecycle
|
|
16
|
+
|
|
17
|
+
The Minitest runner creates declared models and tables in `before_setup` and
|
|
18
|
+
destroys them in `after_teardown`. This makes them available to ordinary setup
|
|
19
|
+
methods and keeps them available through teardown. `scope:` is silently ignored
|
|
20
|
+
by Minitest because it is an RSpec-only option.
|
|
21
|
+
|
|
22
|
+
## Manual setup
|
|
23
|
+
|
|
24
|
+
If another process or tool owns Minitest configuration, configure manually:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "minitest"
|
|
28
|
+
require "with_model"
|
|
29
|
+
|
|
30
|
+
WithModel.runner = :minitest
|
|
31
|
+
Minitest::Test.extend WithModel
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Minitest is an optional consumer dependency. Requiring `with_model/minitest`
|
|
35
|
+
without Minitest installed raises Minitest's ordinary `LoadError`.
|
|
36
|
+
|
|
37
|
+
## Mixed runners
|
|
38
|
+
|
|
39
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
40
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
41
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
42
|
+
declaration with `runner:`.
|
|
43
|
+
|
|
44
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
45
|
+
tables.
|
data/docs/rspec.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# RSpec integration
|
|
2
|
+
|
|
3
|
+
Load the RSpec entrypoint before defining example groups:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/rspec"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The entrypoint requires `rspec/core` and `with_model`, selects the `:rspec`
|
|
10
|
+
runner, and extends RSpec example groups through `RSpec.configure`. It does not
|
|
11
|
+
start RSpec; the RSpec command-line runner remains in control.
|
|
12
|
+
|
|
13
|
+
## Lifecycle
|
|
14
|
+
|
|
15
|
+
`with_model` and `with_table` register RSpec `before` and `after` hooks. Models
|
|
16
|
+
and tables are available to examples after setup and are removed after cleanup.
|
|
17
|
+
Use `scope:` when RSpec hook scope is needed, including `scope: :all` for a
|
|
18
|
+
model needed by `before(:all)`. `scope:` is an RSpec-only option.
|
|
19
|
+
|
|
20
|
+
## Manual setup
|
|
21
|
+
|
|
22
|
+
For runner-owned setup, require the framework and `with_model` separately:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require "rspec/core"
|
|
26
|
+
require "with_model"
|
|
27
|
+
|
|
28
|
+
WithModel.runner = :rspec
|
|
29
|
+
RSpec.configure { |config| config.extend WithModel }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
RSpec is an optional consumer dependency. Requiring `with_model/rspec` without
|
|
33
|
+
RSpec installed raises RSpec's ordinary `LoadError`.
|
|
34
|
+
|
|
35
|
+
## Mixed runners
|
|
36
|
+
|
|
37
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
38
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
39
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
40
|
+
declaration with `runner:`.
|
|
41
|
+
|
|
42
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
43
|
+
tables.
|
data/docs/test-unit.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Test::Unit integration
|
|
2
|
+
|
|
3
|
+
Load the Test::Unit entrypoint before defining tests:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "with_model/test_unit"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The entrypoint requires `test/unit`, then `with_model`, selects the
|
|
10
|
+
`:test_unit` runner, and extends `Test::Unit::TestCase`. Test::Unit retains
|
|
11
|
+
ownership of autorun and fixture execution.
|
|
12
|
+
|
|
13
|
+
## Lifecycle
|
|
14
|
+
|
|
15
|
+
`with_model` and `with_table` register Test::Unit fixture callbacks with exact
|
|
16
|
+
placement: setup uses `before: :append`, while teardown uses `after: :prepend`.
|
|
17
|
+
Declared models and tables therefore create in declaration order before user or
|
|
18
|
+
inherited setup methods and callbacks. They destroy in reverse order after user
|
|
19
|
+
or inherited teardown methods and callbacks.
|
|
20
|
+
|
|
21
|
+
Test::Unit runs teardown when setup raises. `WithModel::Model#destroy`
|
|
22
|
+
defensively handles creation that never assigned a model. `Table#destroy` checks
|
|
23
|
+
whether its table exists before dropping it, so teardown does not raise solely
|
|
24
|
+
because a reconnect replaced an in-memory database or the table otherwise
|
|
25
|
+
already disappeared. It does not rescue other database, connection, or drop
|
|
26
|
+
errors; those still propagate. This conditional drop check does not promise
|
|
27
|
+
generic partial-creation recovery for bare `with_table`. `scope:` is silently
|
|
28
|
+
ignored by Test::Unit because it is an RSpec-only option.
|
|
29
|
+
|
|
30
|
+
## Errata and concerns
|
|
31
|
+
|
|
32
|
+
Establish the Active Record connection before `with_model`'s setup callback
|
|
33
|
+
creates tables and models. An ordinary `def setup` runs too late. Connect in
|
|
34
|
+
your test helper before test classes load, or register a connection callback
|
|
35
|
+
with `setup(before: :prepend)`.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
class PostTest < Test::Unit::TestCase
|
|
39
|
+
setup(before: :prepend) do
|
|
40
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
with_model :Post do
|
|
44
|
+
table
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Manual setup
|
|
50
|
+
|
|
51
|
+
For runner-owned setup, require Test::Unit and `with_model` separately:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require "test/unit"
|
|
55
|
+
require "with_model"
|
|
56
|
+
|
|
57
|
+
WithModel.runner = :test_unit
|
|
58
|
+
Test::Unit::TestCase.extend WithModel
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Test::Unit is an optional consumer dependency. Requiring
|
|
62
|
+
`with_model/test_unit` without Test::Unit installed raises Test::Unit's ordinary
|
|
63
|
+
`LoadError`.
|
|
64
|
+
|
|
65
|
+
## Mixed runners
|
|
66
|
+
|
|
67
|
+
Do not load multiple convenience entrypoints in one process: each changes the
|
|
68
|
+
global runner, and the last one wins. For mixed runners, load plain
|
|
69
|
+
`with_model`, extend each framework boundary directly, and select a runner per
|
|
70
|
+
declaration with `runner:`.
|
|
71
|
+
|
|
72
|
+
See the [general DSL reference](../README.md#usage) for declaring models and
|
|
73
|
+
tables.
|
|
@@ -74,14 +74,6 @@ module WithModel
|
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
module ActiveSupport
|
|
82
|
-
module DescendantsTracker
|
|
83
|
-
class << self
|
|
84
|
-
attr_reader :clear_disabled
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
77
|
+
ActiveRecord::Base.singleton_class.prepend(
|
|
78
|
+
WithModel::DescendantsTracker::DestroyedClassesFiltering
|
|
79
|
+
)
|
data/lib/with_model/model.rb
CHANGED
|
@@ -74,8 +74,6 @@ module WithModel
|
|
|
74
74
|
# can only do so while the class still has its name.
|
|
75
75
|
table.teardown(@model)
|
|
76
76
|
stubber.unstub_const
|
|
77
|
-
cleanup_descendants_tracking
|
|
78
|
-
reset_dependencies_cache
|
|
79
77
|
WithModel::DescendantsTracker.clear([@model])
|
|
80
78
|
@model = nil
|
|
81
79
|
end
|
|
@@ -132,17 +130,6 @@ module WithModel
|
|
|
132
130
|
@model.reset_column_information
|
|
133
131
|
end
|
|
134
132
|
|
|
135
|
-
def cleanup_descendants_tracking
|
|
136
|
-
ActiveSupport::DescendantsTracker.clear([@model]) \
|
|
137
|
-
unless ActiveSupport::DescendantsTracker.clear_disabled
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def reset_dependencies_cache
|
|
141
|
-
return unless defined?(ActiveSupport::Dependencies::Reference)
|
|
142
|
-
|
|
143
|
-
ActiveSupport::Dependencies::Reference.clear!
|
|
144
|
-
end
|
|
145
|
-
|
|
146
133
|
def stubber
|
|
147
134
|
@stubber ||= ConstantStubber.new const_name
|
|
148
135
|
end
|
data/lib/with_model/table.rb
CHANGED
|
@@ -38,19 +38,13 @@ module WithModel
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def destroy
|
|
41
|
-
connection.drop_table(@name)
|
|
41
|
+
connection.drop_table(@name) if exists?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
46
|
attr_reader :connection
|
|
47
47
|
|
|
48
|
-
def exists?
|
|
49
|
-
if connection.respond_to?(:data_source_exists?)
|
|
50
|
-
connection.data_source_exists?(@name)
|
|
51
|
-
else
|
|
52
|
-
connection.table_exists?(@name)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
48
|
+
def exists? = connection.data_source_exists?(@name)
|
|
55
49
|
end
|
|
56
50
|
end
|
data/lib/with_model/version.rb
CHANGED
data/lib/with_model.rb
CHANGED
|
@@ -113,8 +113,13 @@ module WithModel
|
|
|
113
113
|
class_eval do
|
|
114
114
|
include MiniTestLifeCycle.call(object)
|
|
115
115
|
end
|
|
116
|
+
when :test_unit
|
|
117
|
+
# These options place creation before and destruction after user callbacks,
|
|
118
|
+
# preserving declaration and reverse order across inheritance.
|
|
119
|
+
setup(before: :append) { object.create }
|
|
120
|
+
teardown(after: :prepend) { object.destroy }
|
|
116
121
|
else
|
|
117
|
-
raise ArgumentError, "Unsupported test runner set, expected :rspec or :
|
|
122
|
+
raise ArgumentError, "Unsupported test runner set, expected :rspec, :minitest, or :test_unit"
|
|
118
123
|
end
|
|
119
124
|
end
|
|
120
125
|
end
|
|
@@ -30,7 +30,6 @@ describe "Descendants tracking" do
|
|
|
30
30
|
|
|
31
31
|
context "with ActiveSupport::DescendantsTracker (cache_classes: true)" do
|
|
32
32
|
before do
|
|
33
|
-
expect(ActiveSupport::DescendantsTracker.clear_disabled).to be_falsey
|
|
34
33
|
expect { ActiveSupport::DescendantsTracker.clear([]) }.not_to raise_exception
|
|
35
34
|
end
|
|
36
35
|
|
|
@@ -40,10 +39,29 @@ describe "Descendants tracking" do
|
|
|
40
39
|
context "without ActiveSupport::DescendantsTracker (cache_classes: false)" do
|
|
41
40
|
before do
|
|
42
41
|
ActiveSupport::DescendantsTracker.disable_clear!
|
|
43
|
-
expect(ActiveSupport::DescendantsTracker.clear_disabled).to be_truthy
|
|
44
42
|
expect { ActiveSupport::DescendantsTracker.clear([]) }.to raise_exception(RuntimeError)
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
include_examples "clearing descendants between test runs"
|
|
48
46
|
end
|
|
47
|
+
|
|
48
|
+
it "keeps filtering ahead of later class method extensions" do
|
|
49
|
+
raw_subclasses = Class.instance_method(:subclasses).super_method
|
|
50
|
+
raw_descendants = Class.instance_method(:descendants).super_method
|
|
51
|
+
override = Module.new do
|
|
52
|
+
define_method(:subclasses) { raw_subclasses.bind_call(self) }
|
|
53
|
+
define_method(:descendants) { raw_descendants.bind_call(self) }
|
|
54
|
+
end
|
|
55
|
+
destroyed_model = stub_const("DestroyedModel", Class.new(ActiveRecord::Base))
|
|
56
|
+
WithModel::DescendantsTracker.clear([destroyed_model])
|
|
57
|
+
|
|
58
|
+
ActiveRecord::Base.extend override
|
|
59
|
+
|
|
60
|
+
expect(ActiveRecord::Base.descendants).not_to include(destroyed_model)
|
|
61
|
+
ensure
|
|
62
|
+
override&.module_eval do
|
|
63
|
+
remove_method :subclasses
|
|
64
|
+
remove_method :descendants
|
|
65
|
+
end
|
|
66
|
+
end
|
|
49
67
|
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
RSpec.describe "runner integration entrypoints" do
|
|
8
|
+
def run_ruby(source, load_paths: ["lib"])
|
|
9
|
+
Open3.capture3(
|
|
10
|
+
RbConfig.ruby,
|
|
11
|
+
*load_paths.map { |path| "-I#{path}" },
|
|
12
|
+
"-e",
|
|
13
|
+
source
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def expect_native_runner_to_pass(source, load_paths: ["lib"])
|
|
18
|
+
stdout, stderr, status = run_ruby(source, load_paths:)
|
|
19
|
+
|
|
20
|
+
expect(status).to be_success, <<~MESSAGE
|
|
21
|
+
subprocess failed with status #{status.exitstatus}
|
|
22
|
+
stdout:
|
|
23
|
+
#{stdout}
|
|
24
|
+
stderr:
|
|
25
|
+
#{stderr}
|
|
26
|
+
MESSAGE
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "configures RSpec and extends new example groups" do
|
|
30
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
31
|
+
require "with_model/rspec"
|
|
32
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :rspec
|
|
33
|
+
|
|
34
|
+
RSpec.describe "entrypoint" do
|
|
35
|
+
it "installs the DSL" do
|
|
36
|
+
expect(self.class).to respond_to(:with_model)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
exit RSpec::Core::Runner.run([])
|
|
41
|
+
RUBY
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "configures Minitest and extends new test subclasses" do
|
|
45
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
46
|
+
require "with_model/minitest"
|
|
47
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :minitest
|
|
48
|
+
|
|
49
|
+
# Assert that minitest/autorun is not loaded
|
|
50
|
+
autorun_loaded = $LOADED_FEATURES.any? { |f| f.end_with?("/minitest/autorun.rb") }
|
|
51
|
+
abort "autorun should not be loaded; loaded features: #{$LOADED_FEATURES.inspect}" if autorun_loaded
|
|
52
|
+
|
|
53
|
+
class MinitestEntrypointTest < Minitest::Test
|
|
54
|
+
def test_installs_the_dsl
|
|
55
|
+
assert_respond_to self.class, :with_model
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
exit(Minitest.run ? 0 : 1)
|
|
60
|
+
RUBY
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "configures Test::Unit and extends new test subclasses" do
|
|
64
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
65
|
+
require "with_model/test_unit"
|
|
66
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :test_unit
|
|
67
|
+
|
|
68
|
+
class TestUnitEntrypointTest < Test::Unit::TestCase
|
|
69
|
+
def test_installs_the_dsl
|
|
70
|
+
assert_respond_to self.class, :with_model
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
RUBY
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "propagates Test::Unit's LoadError when the framework is unavailable" do
|
|
77
|
+
Dir.mktmpdir do |directory|
|
|
78
|
+
Dir.mkdir(File.join(directory, "test"))
|
|
79
|
+
File.write(
|
|
80
|
+
File.join(directory, "test/unit.rb"),
|
|
81
|
+
'raise LoadError, "distinctive test/unit LoadError"'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
expect_native_runner_to_pass(<<~'RUBY', load_paths: [directory, "lib"])
|
|
85
|
+
begin
|
|
86
|
+
require "with_model/test_unit"
|
|
87
|
+
rescue LoadError => error
|
|
88
|
+
abort "unexpected LoadError: #{error.message.inspect}" unless
|
|
89
|
+
error.message == "distinctive test/unit LoadError"
|
|
90
|
+
else
|
|
91
|
+
abort "expected a LoadError"
|
|
92
|
+
end
|
|
93
|
+
RUBY
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "retains manual Test::Unit setup with plain with_model" do
|
|
98
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
99
|
+
require "test/unit"
|
|
100
|
+
require "with_model"
|
|
101
|
+
|
|
102
|
+
WithModel.runner = :test_unit
|
|
103
|
+
Test::Unit::TestCase.extend WithModel
|
|
104
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :test_unit
|
|
105
|
+
|
|
106
|
+
class PlainWithModelTestUnitTest < Test::Unit::TestCase
|
|
107
|
+
def test_installs_the_dsl
|
|
108
|
+
assert_respond_to self.class, :with_model
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
RUBY
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "retains manual RSpec setup with plain with_model" do
|
|
115
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
116
|
+
require "rspec"
|
|
117
|
+
require "with_model"
|
|
118
|
+
|
|
119
|
+
WithModel.runner = :rspec
|
|
120
|
+
RSpec.configure do |config|
|
|
121
|
+
config.extend WithModel
|
|
122
|
+
end
|
|
123
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :rspec
|
|
124
|
+
|
|
125
|
+
RSpec.describe "entrypoint" do
|
|
126
|
+
it "installs the DSL" do
|
|
127
|
+
expect(self.class).to respond_to(:with_model)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
exit RSpec::Core::Runner.run([])
|
|
132
|
+
RUBY
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "retains manual Minitest setup with plain with_model" do
|
|
136
|
+
expect_native_runner_to_pass(<<~'RUBY')
|
|
137
|
+
require "minitest"
|
|
138
|
+
require "with_model"
|
|
139
|
+
|
|
140
|
+
WithModel.runner = :minitest
|
|
141
|
+
Minitest::Test.extend WithModel
|
|
142
|
+
abort "runner: #{WithModel.runner.inspect}" unless WithModel.runner == :minitest
|
|
143
|
+
|
|
144
|
+
class PlainWithModelMinitestTest < Minitest::Test
|
|
145
|
+
def test_installs_the_dsl
|
|
146
|
+
assert_respond_to self.class, :with_model
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
exit(Minitest.run ? 0 : 1)
|
|
151
|
+
RUBY
|
|
152
|
+
end
|
|
153
|
+
end
|
data/spec/table_false_spec.rb
CHANGED
|
@@ -21,10 +21,6 @@ RSpec.describe "table(false)" do
|
|
|
21
21
|
classes = @stubbed_active_record_classes
|
|
22
22
|
next unless classes
|
|
23
23
|
|
|
24
|
-
ActiveSupport::DescendantsTracker.clear(classes) \
|
|
25
|
-
unless ActiveSupport::DescendantsTracker.clear_disabled
|
|
26
|
-
ActiveSupport::Dependencies::Reference.clear! \
|
|
27
|
-
if defined?(ActiveSupport::Dependencies::Reference)
|
|
28
24
|
WithModel::DescendantsTracker.clear(classes)
|
|
29
25
|
end
|
|
30
26
|
|
data/spec/table_spec.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe WithModel::Table do
|
|
6
|
+
let(:connection_owner) do
|
|
7
|
+
klass = Class.new(ActiveRecord::Base)
|
|
8
|
+
stub_const("TableSpecConnection", klass)
|
|
9
|
+
klass.abstract_class = true
|
|
10
|
+
klass.establish_connection(adapter: "sqlite3", database: ":memory:")
|
|
11
|
+
klass
|
|
12
|
+
end
|
|
13
|
+
let(:connection) { connection_owner.connection }
|
|
14
|
+
let(:table) { described_class.new(:temporary_records, connection:) }
|
|
15
|
+
|
|
16
|
+
after { connection_owner.connection_pool.disconnect! }
|
|
17
|
+
|
|
18
|
+
describe "#destroy" do
|
|
19
|
+
it "does not raise after its connection reconnects before teardown" do
|
|
20
|
+
table.create
|
|
21
|
+
|
|
22
|
+
connection.disconnect!
|
|
23
|
+
connection.reconnect!
|
|
24
|
+
|
|
25
|
+
expect(connection.data_source_exists?(:temporary_records)).to be false
|
|
26
|
+
expect { table.destroy }.not_to raise_error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "drops an existing table" do
|
|
30
|
+
table.create
|
|
31
|
+
|
|
32
|
+
table.destroy
|
|
33
|
+
|
|
34
|
+
expect(connection.data_source_exists?(:temporary_records)).to be false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
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,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
|
|
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", ">=
|
|
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.
|
|
4
|
+
version: 2.4.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: '
|
|
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: '
|
|
28
|
-
description: Dynamically build
|
|
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,41 @@ 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
|
|
69
78
|
- test/spec_dsl_test.rb
|
|
70
79
|
- test/sti_test.rb
|
|
71
80
|
- test/test_helper.rb
|
|
72
81
|
- test/with_model_test.rb
|
|
82
|
+
- test_unit/lifecycle_test.rb
|
|
83
|
+
- test_unit/sti_test.rb
|
|
84
|
+
- test_unit/test_unit_helper.rb
|
|
85
|
+
- test_unit/with_model_test.rb
|
|
73
86
|
- with_model.gemspec
|
|
74
87
|
homepage: https://github.com/Casecommons/with_model
|
|
75
88
|
licenses:
|
|
@@ -90,7 +103,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
90
103
|
- !ruby/object:Gem::Version
|
|
91
104
|
version: '0'
|
|
92
105
|
requirements: []
|
|
93
|
-
rubygems_version: 4.0.
|
|
106
|
+
rubygems_version: 4.0.20
|
|
94
107
|
specification_version: 4
|
|
95
|
-
summary: Dynamically build
|
|
108
|
+
summary: Dynamically build an Active Record model for RSpec, Minitest, and Test::Unit
|
|
109
|
+
tests
|
|
96
110
|
test_files: []
|