fixture_kit 0.4.0 → 0.6.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/README.md +71 -21
- data/lib/fixture_kit/adapter.rb +19 -0
- data/lib/fixture_kit/{isolators/minitest_isolator.rb → adapters/minitest_adapter.rb} +7 -2
- data/lib/fixture_kit/{isolators/rspec_isolator.rb → adapters/rspec_adapter.rb} +9 -2
- data/lib/fixture_kit/cache.rb +32 -12
- data/lib/fixture_kit/configuration.rb +14 -4
- data/lib/fixture_kit/definition.rb +2 -1
- data/lib/fixture_kit/fixture.rb +8 -17
- data/lib/fixture_kit/minitest.rb +14 -4
- data/lib/fixture_kit/registry.rb +35 -14
- data/lib/fixture_kit/rspec.rb +9 -4
- data/lib/fixture_kit/runner.rb +16 -8
- data/lib/fixture_kit/version.rb +1 -1
- data/lib/fixture_kit.rb +5 -3
- metadata +4 -4
- data/lib/fixture_kit/isolator.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c4966344ca2fed5d11b47404694babb874ee5ac505ed64fda79acab147b4230
|
|
4
|
+
data.tar.gz: da1b34e717a3e6aa1b776b6be68e71c104d28612b879daf1b63c30a0b83244e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d127ec7c68e6839bda857231d2fe0f70263d208aad9615834cad70e47ea2cbecd3b270dea7f795d02452a72b0f155694b3c6e78eb589349a639e7158bfcf902
|
|
7
|
+
data.tar.gz: 69149bc49f370405cd89043eced96984cc4a3a3efa3e85ce3e45ba15a4859db3805862b14ebfb7171a7b88059d52e1479b201fbb2ee582b379be6c8ef2de9830
|
data/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Test data setup is slow. Every `Model.create!` or `FactoryBot.create` hits the d
|
|
|
10
10
|
|
|
11
11
|
FixtureKit caches database records as raw SQL INSERT statements. It executes your fixture definition once, captures the resulting database state, and generates optimized batch INSERT statements. Fixture loads then replay these statements directly: no ORM overhead, no callbacks, just fast SQL.
|
|
12
12
|
|
|
13
|
-
Combined with RSpec
|
|
13
|
+
Combined with framework transactions (`use_transactional_fixtures` in RSpec, `use_transactional_tests` in Minitest), each test runs in a transaction that rolls back, so cached data can be reused safely between tests.
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -106,6 +106,22 @@ end
|
|
|
106
106
|
|
|
107
107
|
`fixture` returns a `Repository` and exposes records as methods (for example, `fixture.owner`).
|
|
108
108
|
|
|
109
|
+
You can also define fixtures anonymously inline:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
RSpec.describe Book do
|
|
113
|
+
fixture do
|
|
114
|
+
owner = User.create!(name: "Alice", email: "alice@example.com")
|
|
115
|
+
featured = Book.create!(title: "Dune", owner: owner)
|
|
116
|
+
expose(owner: owner, featured: featured)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "uses inline fixture data" do
|
|
120
|
+
expect(fixture.featured.owner).to eq(fixture.owner)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
109
125
|
### 3. Configure RSpec
|
|
110
126
|
|
|
111
127
|
```ruby
|
|
@@ -117,7 +133,7 @@ RSpec.configure do |config|
|
|
|
117
133
|
end
|
|
118
134
|
```
|
|
119
135
|
|
|
120
|
-
When you call `fixture "name"` in an example group, FixtureKit registers that fixture with its runner.
|
|
136
|
+
When you call `fixture "name"` or `fixture do ... end` in an example group, FixtureKit registers that fixture with its runner.
|
|
121
137
|
|
|
122
138
|
### 4. Configure Minitest
|
|
123
139
|
|
|
@@ -130,7 +146,7 @@ class ActiveSupport::TestCase
|
|
|
130
146
|
end
|
|
131
147
|
```
|
|
132
148
|
|
|
133
|
-
When you call `fixture "name"` in a test class, FixtureKit registers that fixture with its runner and mounts it during test setup.
|
|
149
|
+
When you call `fixture "name"` or `fixture do ... end` in a test class, FixtureKit registers that fixture with its runner and mounts it during test setup.
|
|
134
150
|
|
|
135
151
|
## Configuration
|
|
136
152
|
|
|
@@ -146,39 +162,63 @@ FixtureKit.configure do |config|
|
|
|
146
162
|
# Where cache files are stored (default: tmp/cache/fixture_kit)
|
|
147
163
|
config.cache_path = Rails.root.join("tmp/cache/fixture_kit").to_s
|
|
148
164
|
|
|
149
|
-
#
|
|
150
|
-
# config.
|
|
151
|
-
# config.
|
|
165
|
+
# Adapter used to isolate generation work (default: FixtureKit::MinitestAdapter)
|
|
166
|
+
# config.adapter(FixtureKit::MinitestAdapter)
|
|
167
|
+
# config.adapter(FixtureKit::RSpecAdapter)
|
|
168
|
+
# config.adapter(CustomAdapter, option1: "value1")
|
|
169
|
+
#
|
|
170
|
+
# Calling `adapter` with args sets adapter class + options.
|
|
171
|
+
# Calling `adapter` with no args returns the configured adapter class.
|
|
152
172
|
|
|
153
173
|
# Optional callback, called right before a fixture cache is generated.
|
|
154
174
|
# Called on first generation and forced regeneration.
|
|
155
|
-
# Receives the
|
|
156
|
-
#
|
|
175
|
+
# Receives the cache identifier as a String (path-like, without ".json").
|
|
176
|
+
# - named fixtures: "teams/basic"
|
|
177
|
+
# - anonymous fixtures: "_anonymous/foo/with_fixture_kit/hello"
|
|
178
|
+
# config.on_cache_save = ->(identifier) { puts "cached #{identifier}" }
|
|
179
|
+
|
|
180
|
+
# Optional callback, called right before a fixture cache is mounted.
|
|
181
|
+
# Receives the same String cache identifier as on_cache_save.
|
|
182
|
+
# config.on_cache_mount = ->(identifier) { puts "mounted #{identifier}" }
|
|
157
183
|
end
|
|
158
184
|
```
|
|
159
185
|
|
|
160
|
-
Custom
|
|
161
|
-
`#
|
|
186
|
+
Custom adapters should subclass `FixtureKit::Adapter` and implement:
|
|
187
|
+
- `#execute`
|
|
188
|
+
- `#identifier_for`
|
|
189
|
+
|
|
190
|
+
`#execute` receives the generation block and should run it in whatever lifecycle you need.
|
|
191
|
+
`#identifier_for` receives a non-string fixture identifier (for anonymous fixtures) and must return a normalized String identifier. Cache namespace/prefixing is applied by `FixtureKit::Cache`.
|
|
162
192
|
|
|
163
|
-
|
|
193
|
+
Options passed via `config.adapter(...)` are provided to your adapter initializer as a hash and available as `options`.
|
|
164
194
|
|
|
165
|
-
|
|
195
|
+
By default, FixtureKit uses `FixtureKit::MinitestAdapter`, which runs generation inside an internal `ActiveSupport::TestCase` and removes that harness case from minitest runnables.
|
|
196
|
+
|
|
197
|
+
When using `fixture_kit/rspec`, FixtureKit sets `FixtureKit::RSpecAdapter`. It runs generation inside an internal RSpec example, and uses a null reporter so harness runs do not count toward suite example totals.
|
|
166
198
|
|
|
167
199
|
## Lifecycle
|
|
168
200
|
|
|
169
201
|
Fixture generation is managed by `FixtureKit::Runner`.
|
|
170
202
|
|
|
171
|
-
1. Calling `fixture "name"` registers the fixture with the runner.
|
|
203
|
+
1. Calling `fixture "name"` or `fixture do ... end` registers the fixture with the runner.
|
|
172
204
|
2. Runner `start`:
|
|
173
|
-
- clears `cache_path` (unless preserve-cache is enabled)
|
|
174
|
-
|
|
175
|
-
|
|
205
|
+
- clears `cache_path` (unless preserve-cache is enabled).
|
|
206
|
+
3. Cache generation is framework-driven:
|
|
207
|
+
- RSpec: each declaring example group runs `fixture.cache` in `before(:context)`.
|
|
208
|
+
- Minitest: each declaring test class runs `fixture.cache` in class-level `run_suite` before test methods execute.
|
|
176
209
|
4. At test runtime, `fixture` mounts from cache and returns a `Repository`.
|
|
177
210
|
|
|
178
211
|
When runner start happens:
|
|
179
212
|
|
|
180
213
|
- `fixture_kit/rspec`: in `before(:suite)`.
|
|
181
|
-
- `fixture_kit/minitest`:
|
|
214
|
+
- `fixture_kit/minitest`: in class-level `run_suite` for test classes that declare `fixture`.
|
|
215
|
+
|
|
216
|
+
## Fixture Declaration Rules
|
|
217
|
+
|
|
218
|
+
- Only one `fixture` declaration is allowed per test context.
|
|
219
|
+
- Declaring a fixture twice in the same context raises `FixtureKit::MultipleFixtures`.
|
|
220
|
+
- Child contexts/classes can declare their own fixture and override parent declarations.
|
|
221
|
+
- Providing both a name and a block (or neither) raises `FixtureKit::InvalidFixtureDeclaration`.
|
|
182
222
|
|
|
183
223
|
### Preserving Cache Locally
|
|
184
224
|
|
|
@@ -207,9 +247,19 @@ end
|
|
|
207
247
|
fixture "teams/sales"
|
|
208
248
|
```
|
|
209
249
|
|
|
250
|
+
## Anonymous Fixture Cache Paths
|
|
251
|
+
|
|
252
|
+
Anonymous fixture caches are written under the `_anonymous/` directory inside `cache_path`.
|
|
253
|
+
That `_anonymous/...` value is also the cache identifier passed to `on_cache_save` and `on_cache_mount`.
|
|
254
|
+
|
|
255
|
+
- Minitest: class name is underscored into a path.
|
|
256
|
+
- `MyFeatureTest` -> `_anonymous/my_feature_test.json`
|
|
257
|
+
- RSpec: class name is underscored after removing `RSpec::ExampleGroups::`.
|
|
258
|
+
- `RSpec::ExampleGroups::Foo::WithFixtureKit::Hello` -> `_anonymous/foo/with_fixture_kit/hello.json`
|
|
259
|
+
|
|
210
260
|
## How It Works
|
|
211
261
|
|
|
212
|
-
1. **Cache generation**: FixtureKit executes your definition block inside the configured
|
|
262
|
+
1. **Cache generation**: FixtureKit executes your definition block inside the configured adapter, subscribes to `sql.active_record` notifications to track model writes (`Insert`, `Upsert`, `Create`, `Update`, `Delete`, `Destroy`, including bulk variants), queries those model tables, and caches SQL statements for current table contents.
|
|
213
263
|
|
|
214
264
|
2. **Mounting**: FixtureKit loads the cached JSON file, clears each tracked table, and executes the raw SQL INSERT statements directly. No ORM instantiation, no callbacks.
|
|
215
265
|
|
|
@@ -224,8 +274,8 @@ Caches are stored as JSON files in `tmp/cache/fixture_kit/`:
|
|
|
224
274
|
```json
|
|
225
275
|
{
|
|
226
276
|
"records": {
|
|
227
|
-
"User": "INSERT
|
|
228
|
-
"Project": "INSERT
|
|
277
|
+
"User": "INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com'), (2, 'Bob', 'bob@example.com')",
|
|
278
|
+
"Project": "INSERT INTO projects (id, name, user_id) VALUES (1, 'Website', 1)"
|
|
229
279
|
},
|
|
230
280
|
"exposed": {
|
|
231
281
|
"alice": { "model": "User", "id": 1 },
|
|
@@ -235,7 +285,7 @@ Caches are stored as JSON files in `tmp/cache/fixture_kit/`:
|
|
|
235
285
|
}
|
|
236
286
|
```
|
|
237
287
|
|
|
238
|
-
- **records**: Maps model names to their INSERT statements. Using model names (not table names) allows FixtureKit to use the correct database connection for multi-database setups.
|
|
288
|
+
- **records**: Maps model names to their INSERT statements (or `null` when a tracked table is empty). Using model names (not table names) allows FixtureKit to use the correct database connection for multi-database setups.
|
|
239
289
|
- **exposed**: Maps fixture accessor names to their model class and ID for querying after cache replay.
|
|
240
290
|
|
|
241
291
|
## Cache Management
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FixtureKit
|
|
4
|
+
class Adapter
|
|
5
|
+
attr_reader :options
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute
|
|
12
|
+
raise NotImplementedError, "#{self.class} must implement #execute"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def identifier_for(_identifier)
|
|
16
|
+
raise NotImplementedError, "#{self.class} must implement #identifier_for"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_support/test_case"
|
|
4
4
|
require "active_record/fixtures"
|
|
5
|
+
require "active_support/inflector"
|
|
5
6
|
|
|
6
7
|
module FixtureKit
|
|
7
|
-
class
|
|
8
|
+
class MinitestAdapter < FixtureKit::Adapter
|
|
8
9
|
TEST_NAME = "fixture kit cache pregeneration"
|
|
9
10
|
|
|
10
|
-
def
|
|
11
|
+
def execute(&block)
|
|
11
12
|
test_class = build_test_class
|
|
12
13
|
test_method = test_class.test(TEST_NAME) do
|
|
13
14
|
block.call
|
|
@@ -20,6 +21,10 @@ module FixtureKit
|
|
|
20
21
|
raise result.failures.first.error
|
|
21
22
|
end
|
|
22
23
|
|
|
24
|
+
def identifier_for(identifier)
|
|
25
|
+
ActiveSupport::Inflector.underscore(identifier.to_s)
|
|
26
|
+
end
|
|
27
|
+
|
|
23
28
|
private
|
|
24
29
|
|
|
25
30
|
def build_test_class
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/inflector"
|
|
4
|
+
|
|
3
5
|
module FixtureKit
|
|
4
|
-
class
|
|
5
|
-
def
|
|
6
|
+
class RSpecAdapter < FixtureKit::Adapter
|
|
7
|
+
def execute(&block)
|
|
6
8
|
previous_example = ::RSpec.current_example
|
|
7
9
|
previous_scope = ::RSpec.current_scope
|
|
8
10
|
example_group = build_example_group
|
|
@@ -19,6 +21,11 @@ module FixtureKit
|
|
|
19
21
|
raise example.exception unless succeeded
|
|
20
22
|
end
|
|
21
23
|
|
|
24
|
+
def identifier_for(identifier)
|
|
25
|
+
normalized_scope = identifier.to_s.sub(/\ARSpec::ExampleGroups::/, "")
|
|
26
|
+
ActiveSupport::Inflector.underscore(normalized_scope)
|
|
27
|
+
end
|
|
28
|
+
|
|
22
29
|
private
|
|
23
30
|
|
|
24
31
|
def build_example_group
|
data/lib/fixture_kit/cache.rb
CHANGED
|
@@ -7,17 +7,29 @@ require "active_support/inflector"
|
|
|
7
7
|
|
|
8
8
|
module FixtureKit
|
|
9
9
|
class Cache
|
|
10
|
+
ANONYMOUS_DIRECTORY = "_anonymous"
|
|
11
|
+
|
|
10
12
|
include ConfigurationHelper
|
|
11
13
|
|
|
12
14
|
attr_reader :fixture
|
|
13
15
|
|
|
14
|
-
def initialize(fixture
|
|
16
|
+
def initialize(fixture)
|
|
15
17
|
@fixture = fixture
|
|
16
|
-
@definition = definition
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def path
|
|
20
|
-
File.join(configuration.cache_path, "#{
|
|
21
|
+
File.join(configuration.cache_path, "#{identifier}.json")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def identifier
|
|
25
|
+
@identifier ||= begin
|
|
26
|
+
raw_identifier = fixture.identifier
|
|
27
|
+
if raw_identifier.is_a?(String)
|
|
28
|
+
raw_identifier
|
|
29
|
+
else
|
|
30
|
+
File.join(ANONYMOUS_DIRECTORY, FixtureKit.runner.adapter.identifier_for(raw_identifier))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
21
33
|
end
|
|
22
34
|
|
|
23
35
|
def exists?
|
|
@@ -26,17 +38,15 @@ module FixtureKit
|
|
|
26
38
|
|
|
27
39
|
def load
|
|
28
40
|
unless exists?
|
|
29
|
-
raise FixtureKit::CacheMissingError, "Cache does not exist for fixture '#{fixture.
|
|
41
|
+
raise FixtureKit::CacheMissingError, "Cache does not exist for fixture '#{fixture.identifier}'"
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
@data ||= JSON.parse(File.read(path))
|
|
33
|
-
@data.fetch("records").each do |
|
|
34
|
-
model = ActiveSupport::Inflector.constantize(model_name)
|
|
35
|
-
connection = model.connection
|
|
45
|
+
statements_by_connection(@data.fetch("records")).each do |connection, statements|
|
|
36
46
|
connection.disable_referential_integrity do
|
|
37
47
|
# execute_batch is private in current supported Rails versions.
|
|
38
48
|
# This should be revisited when Rails 8.2 makes it public.
|
|
39
|
-
connection.__send__(:execute_batch,
|
|
49
|
+
connection.__send__(:execute_batch, statements, "FixtureKit Load")
|
|
40
50
|
end
|
|
41
51
|
end
|
|
42
52
|
|
|
@@ -44,14 +54,14 @@ module FixtureKit
|
|
|
44
54
|
end
|
|
45
55
|
|
|
46
56
|
def save
|
|
47
|
-
FixtureKit.runner.
|
|
57
|
+
FixtureKit.runner.adapter.execute do
|
|
48
58
|
models = SqlSubscriber.capture do
|
|
49
|
-
|
|
59
|
+
fixture.definition.evaluate
|
|
50
60
|
end
|
|
51
61
|
|
|
52
62
|
@data = {
|
|
53
63
|
"records" => generate_statements(models),
|
|
54
|
-
"exposed" => build_exposed_mapping(
|
|
64
|
+
"exposed" => build_exposed_mapping(fixture.definition.exposed)
|
|
55
65
|
}
|
|
56
66
|
end
|
|
57
67
|
|
|
@@ -77,7 +87,7 @@ module FixtureKit
|
|
|
77
87
|
model.find(id)
|
|
78
88
|
rescue ActiveRecord::RecordNotFound
|
|
79
89
|
raise FixtureKit::ExposedRecordNotFound,
|
|
80
|
-
"Could not find #{model_name} with id=#{id} for exposed record '#{exposed_name}' in fixture '#{@fixture.
|
|
90
|
+
"Could not find #{model_name} with id=#{id} for exposed record '#{exposed_name}' in fixture '#{@fixture.identifier}'"
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
def generate_statements(models)
|
|
@@ -120,5 +130,15 @@ module FixtureKit
|
|
|
120
130
|
hash[name] = was_array ? records : records.first
|
|
121
131
|
end
|
|
122
132
|
end
|
|
133
|
+
|
|
134
|
+
def statements_by_connection(records)
|
|
135
|
+
records.each_with_object({}) do |(model_name, sql), grouped|
|
|
136
|
+
model = ActiveSupport::Inflector.constantize(model_name)
|
|
137
|
+
connection = model.connection
|
|
138
|
+
grouped[connection] ||= []
|
|
139
|
+
grouped[connection] << build_delete_sql(model)
|
|
140
|
+
grouped[connection] << sql if sql
|
|
141
|
+
end
|
|
142
|
+
end
|
|
123
143
|
end
|
|
124
144
|
end
|
|
@@ -4,14 +4,17 @@ module FixtureKit
|
|
|
4
4
|
class Configuration
|
|
5
5
|
attr_writer :fixture_path
|
|
6
6
|
attr_writer :cache_path
|
|
7
|
-
attr_accessor :
|
|
8
|
-
attr_accessor :
|
|
7
|
+
attr_accessor :on_cache_save
|
|
8
|
+
attr_accessor :on_cache_mount
|
|
9
|
+
attr_reader :adapter_options
|
|
9
10
|
|
|
10
11
|
def initialize
|
|
11
12
|
@fixture_path = "fixture_kit"
|
|
12
13
|
@cache_path = nil
|
|
13
|
-
@
|
|
14
|
-
@
|
|
14
|
+
@adapter_class = FixtureKit::MinitestAdapter
|
|
15
|
+
@adapter_options = {}
|
|
16
|
+
@on_cache_save = nil
|
|
17
|
+
@on_cache_mount = nil
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def fixture_path
|
|
@@ -22,6 +25,13 @@ module FixtureKit
|
|
|
22
25
|
@cache_path ||= detect_cache_path
|
|
23
26
|
end
|
|
24
27
|
|
|
28
|
+
def adapter(adapter_class = nil, **options)
|
|
29
|
+
return @adapter_class if adapter_class.nil? && options.empty?
|
|
30
|
+
|
|
31
|
+
@adapter_class = adapter_class
|
|
32
|
+
@adapter_options = options
|
|
33
|
+
end
|
|
34
|
+
|
|
25
35
|
private
|
|
26
36
|
|
|
27
37
|
def detect_cache_path
|
data/lib/fixture_kit/fixture.rb
CHANGED
|
@@ -4,37 +4,28 @@ module FixtureKit
|
|
|
4
4
|
class Fixture
|
|
5
5
|
include ConfigurationHelper
|
|
6
6
|
|
|
7
|
-
attr_reader :
|
|
7
|
+
attr_reader :identifier, :definition
|
|
8
8
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@
|
|
11
|
-
@
|
|
12
|
-
@cache = Cache.new(self
|
|
9
|
+
def initialize(identifier, definition)
|
|
10
|
+
@identifier = identifier
|
|
11
|
+
@definition = definition
|
|
12
|
+
@cache = Cache.new(self)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def cache(force: false)
|
|
16
16
|
return if @cache.exists? && !force
|
|
17
17
|
|
|
18
|
-
configuration.
|
|
18
|
+
configuration.on_cache_save&.call(@cache.identifier)
|
|
19
19
|
@cache.save
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def mount
|
|
23
23
|
unless @cache.exists?
|
|
24
|
-
raise FixtureKit::CacheMissingError, "Cache does not exist for fixture '#{
|
|
24
|
+
raise FixtureKit::CacheMissingError, "Cache does not exist for fixture '#{identifier}'"
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
configuration.on_cache_mount&.call(@cache.identifier)
|
|
27
28
|
@cache.load
|
|
28
29
|
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def definition
|
|
33
|
-
@definition ||= begin
|
|
34
|
-
definition = eval(File.read(@path), TOPLEVEL_BINDING.dup, @path)
|
|
35
|
-
raise FixtureKit::FixtureDefinitionNotFound, "Could not find fixture definition at '#{@path}'" unless definition.is_a?(Definition)
|
|
36
|
-
definition
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
30
|
end
|
|
40
31
|
end
|
data/lib/fixture_kit/minitest.rb
CHANGED
|
@@ -8,8 +8,19 @@ module FixtureKit
|
|
|
8
8
|
DECLARATION_CLASS_ATTRIBUTE = :fixture_kit_declaration
|
|
9
9
|
|
|
10
10
|
module ClassMethods
|
|
11
|
-
def fixture(name)
|
|
12
|
-
self.fixture_kit_declaration = FixtureKit.runner.register(name)
|
|
11
|
+
def fixture(name = nil, &definition_block)
|
|
12
|
+
self.fixture_kit_declaration = FixtureKit.runner.register(self, name, &definition_block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run_suite(reporter, options = {})
|
|
16
|
+
declaration = fixture_kit_declaration
|
|
17
|
+
if declaration && !filter_runnable_methods(options).empty?
|
|
18
|
+
runner = FixtureKit.runner
|
|
19
|
+
runner.start unless runner.started?
|
|
20
|
+
declaration.cache
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
super
|
|
13
24
|
end
|
|
14
25
|
end
|
|
15
26
|
|
|
@@ -21,7 +32,7 @@ module FixtureKit
|
|
|
21
32
|
|
|
22
33
|
def self.configure!(test_case)
|
|
23
34
|
FixtureKit.runner.configuration.fixture_path = "test/fixture_kit"
|
|
24
|
-
FixtureKit.runner.configuration.
|
|
35
|
+
FixtureKit.runner.configuration.adapter(FixtureKit::MinitestAdapter)
|
|
25
36
|
|
|
26
37
|
test_case.class_attribute DECLARATION_CLASS_ATTRIBUTE, instance_accessor: false
|
|
27
38
|
test_case.extend ClassMethods
|
|
@@ -31,7 +42,6 @@ module FixtureKit
|
|
|
31
42
|
declaration = self.class.fixture_kit_declaration
|
|
32
43
|
next unless declaration
|
|
33
44
|
|
|
34
|
-
FixtureKit.runner.start unless FixtureKit.runner.started?
|
|
35
45
|
@_fixture_kit_repository = declaration.mount
|
|
36
46
|
end
|
|
37
47
|
end
|
data/lib/fixture_kit/registry.rb
CHANGED
|
@@ -1,35 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "pathname"
|
|
4
|
-
|
|
5
3
|
module FixtureKit
|
|
6
4
|
class Registry
|
|
7
5
|
include ConfigurationHelper
|
|
8
6
|
|
|
9
7
|
def initialize
|
|
10
|
-
@
|
|
8
|
+
@declarations = {}
|
|
9
|
+
@fixtures = {}
|
|
11
10
|
end
|
|
12
11
|
|
|
13
|
-
def add(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
file_path = fixture_file_path(name)
|
|
17
|
-
unless File.file?(file_path)
|
|
18
|
-
raise FixtureKit::FixtureDefinitionNotFound,
|
|
19
|
-
"Could not find fixture definition file for '#{name}' at '#{file_path}'"
|
|
12
|
+
def add(scope, name_or_block)
|
|
13
|
+
if @declarations.key?(scope)
|
|
14
|
+
raise FixtureKit::MultipleFixtures, "cannot load multiple fixtures in the same context"
|
|
20
15
|
end
|
|
21
16
|
|
|
22
|
-
@
|
|
17
|
+
@declarations[scope] =
|
|
18
|
+
case name_or_block
|
|
19
|
+
when String
|
|
20
|
+
fetch_named_fixture(name_or_block)
|
|
21
|
+
when Proc
|
|
22
|
+
fetch_anonymous_fixture(scope, name_or_block)
|
|
23
|
+
else
|
|
24
|
+
raise FixtureKit::InvalidFixtureDeclaration, "unsupported fixture declaration type: #{name_or_block.class}"
|
|
25
|
+
end
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def fixtures
|
|
26
|
-
@
|
|
29
|
+
@fixtures.values
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
private
|
|
30
33
|
|
|
31
|
-
def
|
|
32
|
-
|
|
34
|
+
def fetch_named_fixture(name)
|
|
35
|
+
@fixtures[name] ||= Fixture.new(name, load_named_definition(name))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def fetch_anonymous_fixture(scope, definition)
|
|
39
|
+
@fixtures[scope] ||= Fixture.new(scope, Definition.new(&definition))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def load_named_definition(name)
|
|
43
|
+
file_path = File.expand_path(File.join(configuration.fixture_path, "#{name}.rb"))
|
|
44
|
+
|
|
45
|
+
unless File.file?(file_path)
|
|
46
|
+
raise FixtureKit::FixtureDefinitionNotFound,
|
|
47
|
+
"cannot find fixture definition file for '#{name}' at '#{file_path}'"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
definition = eval(File.read(file_path), TOPLEVEL_BINDING.dup, file_path)
|
|
51
|
+
return definition if definition.is_a?(Definition)
|
|
52
|
+
|
|
53
|
+
raise FixtureKit::FixtureDefinitionNotFound, "cannot find fixture definition at '#{file_path}'"
|
|
33
54
|
end
|
|
34
55
|
end
|
|
35
56
|
end
|
data/lib/fixture_kit/rspec.rb
CHANGED
|
@@ -27,8 +27,13 @@ module FixtureKit
|
|
|
27
27
|
# end
|
|
28
28
|
# end
|
|
29
29
|
# end
|
|
30
|
-
def fixture(name)
|
|
31
|
-
|
|
30
|
+
def fixture(name = nil, &definition_block)
|
|
31
|
+
declaration = ::RSpec.configuration.fixture_kit.register(self, name, &definition_block)
|
|
32
|
+
metadata[DECLARATION_METADATA_KEY] = declaration
|
|
33
|
+
|
|
34
|
+
prepend_before(:context) do
|
|
35
|
+
self.class.metadata[DECLARATION_METADATA_KEY].cache
|
|
36
|
+
end
|
|
32
37
|
end
|
|
33
38
|
end
|
|
34
39
|
|
|
@@ -42,9 +47,9 @@ module FixtureKit
|
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def self.configure!(config)
|
|
45
|
-
FixtureKit.runner.configuration.fixture_path = "spec/fixture_kit"
|
|
46
50
|
config.add_setting(:fixture_kit, default: FixtureKit.runner)
|
|
47
|
-
FixtureKit.runner.configuration.
|
|
51
|
+
FixtureKit.runner.configuration.fixture_path = "spec/fixture_kit"
|
|
52
|
+
FixtureKit.runner.configuration.adapter(FixtureKit::RSpecAdapter)
|
|
48
53
|
|
|
49
54
|
config.extend ClassMethods
|
|
50
55
|
config.include InstanceMethods, DECLARATION_METADATA_KEY
|
data/lib/fixture_kit/runner.rb
CHANGED
|
@@ -11,14 +11,12 @@ module FixtureKit
|
|
|
11
11
|
def initialize
|
|
12
12
|
@configuration = Configuration.new
|
|
13
13
|
@registry = Registry.new
|
|
14
|
-
@
|
|
14
|
+
@adapter = nil
|
|
15
15
|
@started = false
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def register(name)
|
|
19
|
-
registry.add(name)
|
|
20
|
-
fixture.cache if started?
|
|
21
|
-
end
|
|
18
|
+
def register(scope, name = nil, &definition_block)
|
|
19
|
+
registry.add(scope, normalize_registration(name, definition_block))
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def start
|
|
@@ -26,11 +24,10 @@ module FixtureKit
|
|
|
26
24
|
@started = true
|
|
27
25
|
|
|
28
26
|
clear_cache unless preserve_cache?
|
|
29
|
-
registry.fixtures.each(&:cache)
|
|
30
27
|
end
|
|
31
28
|
|
|
32
|
-
def
|
|
33
|
-
@
|
|
29
|
+
def adapter
|
|
30
|
+
@adapter ||= configuration.adapter.new(configuration.adapter_options)
|
|
34
31
|
end
|
|
35
32
|
|
|
36
33
|
def started?
|
|
@@ -46,5 +43,16 @@ module FixtureKit
|
|
|
46
43
|
def preserve_cache?
|
|
47
44
|
ENV[PRESERVE_CACHE_ENV_KEY].to_s.match?(/\A(1|true|yes)\z/i)
|
|
48
45
|
end
|
|
46
|
+
|
|
47
|
+
def normalize_registration(name, definition_block)
|
|
48
|
+
if name && definition_block
|
|
49
|
+
raise FixtureKit::InvalidFixtureDeclaration, "cannot provide both fixture name and definition block"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
name_or_block = name || definition_block
|
|
53
|
+
return name_or_block if name_or_block
|
|
54
|
+
|
|
55
|
+
raise FixtureKit::InvalidFixtureDeclaration, "must provide fixture name or definition block"
|
|
56
|
+
end
|
|
49
57
|
end
|
|
50
58
|
end
|
data/lib/fixture_kit/version.rb
CHANGED
data/lib/fixture_kit.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module FixtureKit
|
|
4
4
|
class Error < StandardError; end
|
|
5
5
|
class DuplicateNameError < Error; end
|
|
6
|
+
class InvalidFixtureDeclaration < Error; end
|
|
7
|
+
class MultipleFixtures < Error; end
|
|
6
8
|
class CacheMissingError < Error; end
|
|
7
9
|
class FixtureDefinitionNotFound < Error; end
|
|
8
10
|
class ExposedRecordNotFound < Error; end
|
|
@@ -19,9 +21,9 @@ module FixtureKit
|
|
|
19
21
|
autoload :SqlSubscriber, File.expand_path("fixture_kit/sql_subscriber", __dir__)
|
|
20
22
|
autoload :Cache, File.expand_path("fixture_kit/cache", __dir__)
|
|
21
23
|
autoload :Runner, File.expand_path("fixture_kit/runner", __dir__)
|
|
22
|
-
autoload :
|
|
23
|
-
autoload :
|
|
24
|
-
autoload :
|
|
24
|
+
autoload :Adapter, File.expand_path("fixture_kit/adapter", __dir__)
|
|
25
|
+
autoload :MinitestAdapter, File.expand_path("fixture_kit/adapters/minitest_adapter", __dir__)
|
|
26
|
+
autoload :RSpecAdapter, File.expand_path("fixture_kit/adapters/rspec_adapter", __dir__)
|
|
25
27
|
|
|
26
28
|
extend Singleton
|
|
27
29
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fixture_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ngan Pham
|
|
@@ -134,14 +134,14 @@ files:
|
|
|
134
134
|
- LICENSE
|
|
135
135
|
- README.md
|
|
136
136
|
- lib/fixture_kit.rb
|
|
137
|
+
- lib/fixture_kit/adapter.rb
|
|
138
|
+
- lib/fixture_kit/adapters/minitest_adapter.rb
|
|
139
|
+
- lib/fixture_kit/adapters/rspec_adapter.rb
|
|
137
140
|
- lib/fixture_kit/cache.rb
|
|
138
141
|
- lib/fixture_kit/configuration.rb
|
|
139
142
|
- lib/fixture_kit/configuration_helper.rb
|
|
140
143
|
- lib/fixture_kit/definition.rb
|
|
141
144
|
- lib/fixture_kit/fixture.rb
|
|
142
|
-
- lib/fixture_kit/isolator.rb
|
|
143
|
-
- lib/fixture_kit/isolators/minitest_isolator.rb
|
|
144
|
-
- lib/fixture_kit/isolators/rspec_isolator.rb
|
|
145
145
|
- lib/fixture_kit/minitest.rb
|
|
146
146
|
- lib/fixture_kit/registry.rb
|
|
147
147
|
- lib/fixture_kit/repository.rb
|
data/lib/fixture_kit/isolator.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module FixtureKit
|
|
4
|
-
# Base class for fixture cache isolators.
|
|
5
|
-
class Isolator
|
|
6
|
-
def self.run(&block)
|
|
7
|
-
new.run(&block)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def run
|
|
11
|
-
raise NotImplementedError, "#{self.class} must implement #run"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|