Skip to content

Commit

Permalink
Refactor (#16)
Browse files Browse the repository at this point in the history
* refactor, remove unnecessary behaviors

* hook up to a db

* clean up schema, simplify

* continue refactor, thinning out schema

* integrate with minitest

* release beta

* more documentaiton and cleanup

* thread safety, move to configuration class

* move things around a bit more

* make files_changed? public

* bundle update

* more documentation, single top level variables

* allow strict_dependencies to be an option

* do not blow up if fixtury overrides test method

* bump to beta7

* add dependabot and test automation (#19)

---------

Co-authored-by: Aaron Averbuch <[email protected]>
  • Loading branch information
mnelson and ahaverbuch authored Apr 22, 2024
1 parent ce912f8 commit e554256
Show file tree
Hide file tree
Showing 57 changed files with 2,392 additions and 1,339 deletions.
24 changes: 24 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
open-pull-requests-limit: 20
schedule:
interval: "daily"
time: "09:00"
timezone: "America/New_York"
commit-message:
prefix: "[github-actions] "
- package-ecosystem: "bundler"
directory: "/"
schedule:
interval: "daily"
time: "08:30"
timezone: "America/New_York"
allow:
- dependency-type: "all"
versioning-strategy: increase
open-pull-requests-limit: 20
insecure-external-code-execution: deny
commit-message:
prefix: "[bundler] "
23 changes: 23 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: build
on:
pull_request:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby-version: [3.2.2]
experimental: [false]
steps:
- uses: actions/checkout@v4
with:
show-progress: 'false'
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs `bundle install` and caches installed gems automatically
- run: bundle exec rake
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.5.1
3.2.2
92 changes: 48 additions & 44 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,64 +1,68 @@
PATH
remote: .
specs:
fixtury (0.4.1)
fixtury (1.0.0.beta7)

GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.3.1)
activemodel (7.1.3.2)
activesupport (= 7.1.3.2)
activerecord (7.1.3.2)
activemodel (= 7.1.3.2)
activesupport (= 7.1.3.2)
timeout (>= 0.4.0)
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
ansi (1.5.0)
autotest (5.0.0)
minitest-autotest (~> 1.0)
builder (3.2.3)
byebug (11.0.1)
concurrent-ruby (1.1.6)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
base64 (0.2.0)
bigdecimal (3.1.6)
byebug (11.1.3)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
drb (2.2.1)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
m (1.6.2)
method_source (>= 0.6.7)
rake (>= 0.9.2.2)
method_source (1.0.0)
mini_portile2 (2.8.5)
minitest (5.22.2)
mocha (2.1.0)
ruby2_keywords (>= 0.0.5)
mutex_m (0.2.0)
rake (13.1.0)
ruby2_keywords (0.0.5)
sqlite3 (1.7.2)
mini_portile2 (~> 2.8.0)
timeout (0.4.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
metaclass (0.0.4)
minitest (5.13.0)
minitest-autotest (1.1.1)
minitest-server (~> 1.0)
path_expander (~> 1.0)
minitest-reporters (1.4.2)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
minitest-server (1.0.5)
minitest (~> 5.0)
mocha (1.8.0)
metaclass (~> 0.0.1)
path_expander (1.1.0)
rake (13.0.1)
ruby-progressbar (1.10.1)
sqlite (1.0.2)
thread_safe (0.3.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
zeitwerk (2.3.0)

PLATFORMS
ruby

DEPENDENCIES
autotest
bundler (~> 2.0)
activerecord
bundler
byebug
fixtury!
globalid
minitest (~> 5.0)
minitest-reporters
m
minitest
mocha
rake (~> 13.0)
sqlite
rake
sqlite3

BUNDLED WITH
2.1.4
2.5.6
102 changes: 94 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# Fixtury

Fixtury aims to provide an interface for creating, managing, and accessing test data in a simple and efficient way. It has no opinion on how you generate the data, it simply provides efficient ways to access it.
Fixtury aims to provide an interface for creating, managing, and accessing test data in a simple and on-demand way. It has no opinion on how you generate the data, it simply provides efficient ways to access it.

Often, fixture frameworks require you to either heavily maintain static fixtures or generate all your fixtures at runtime. Fixtury attempts to find a middle ground that enables a faster and more effecient development process.
Often, fixture frameworks require you to either heavily maintain static fixtures or generate all your fixtures at runtime. Fixtury attempts to find a middle ground that enables a faster and more effecient development process while allowing you to generate realistic test data.

For example, if a developer is running a test locally in their development environment there's no reason to build all fixtures for your suite of 30k tests. Instead, if we're able to track the fixture dependencies of the tests that are running we can build (and cache) the data relevant for the specific tests that are run.

```ruby
class MyTest < ::ActiveSupport::TestCase
include ::Fixtury::TestHooks
require "fixtury/minitest_hooks"

fixtury "users.fresh"
let(:user) { fixtury("users.fresh") }
class MyTest < ::Minitest::Test
prepend ::Fixtury::MintestHooks

fixtury "users/fresh", as: :user

def test_whatever
assert_eq "Doug", user.first_name
Expand All @@ -20,6 +21,91 @@ class MyTest < ::ActiveSupport::TestCase
end
```

Loading this file would ensure `users.fresh` is loaded into the fixture set before the suite is run. In the context of ActiveSupport::TestCase, the Fixtury::Hooks file will ensure the database records are present prior to your suite running. Setting `use_transactional_fixtures` ensures all records are rolled back prior to running another test.
Loading this file would ensure `users/fresh` is loaded into the fixture set before the suite is run. In the context of Minitest::Test, the Fixtury::MinitestHooks file will ensure the database records are present prior to your suite running. If you're using ActiveRecord `use_transactional_fixtures` and `use_transactional_tests` will be respected.

## Configuration

If you're using Rails, you can `require "fixtury/railtie"` to accomplish a standard installation which will observe common rails files for changes and expects fixture definitions to defined in `test/fixtures`. See the railtie class for details.

For non-rails environments or additional configuration, you can open up the Fixtury configuration like so:
```ruby
::Fixtury.configure do |config|
config.locator_backend = :global_id # the locator behavior to use for finding fixtures
config.filepath = File.join(root, "tmp/fixtury.yml") # the location to dump the fixtury references
config.add_fixture_path = File.join(root, "fixtures/**/*.rb")
config.add_dependency_path = File.join(root, "db/schema.rb")
end
```
See Fixtury::Configuration for all options.

When your Fixtury is configured, you should call `Fixtury.start`.

For minitest integration, you should dump the configuration file after the suite runs or after your fixture dependencies are built:

```ruby
::Minitest.after_run do
::Fixtury.configuration.dump_file
end
```

In a CI environment, we'd likely want to preload all fixtures to produce a database snapshot to be shared. This can be done by configuring Fixtury, calling `Fixtury.start`, then calling `Fixtury.load_all_fixtures`. All fixtures declared in the configuration's fixture_paths will be loaded.

## Defining Fixtures

There are two primary principals in Fixtury: namespaces and fixture definitions. See below for an example of how they're used.

```ruby
Fixtury.define do

fixture "user" do
User.create(...)
end

namespace "addresses" do
fixture "sample" do
Address.create(...)
end
end

namespace "user_with_address" do
fixture "user", deps: "address" do |deps|
User.create(address_id: deps.address.id, ...)
end

fixture "address" do
Address.create(...)
end
end
end
```

As you can see fixtures are named in a nested structure and can refer to each other via dependencies. See Fixtury::Dependency for more specifics.

## Isolation Levels

Isolation keys enable groups of fixtures to use and modify the same resources. When one fixture from an isolation level is built, all fixtures in that isolation level are built. This allows multiple fixtures to potentially mutate a resource while keeping the definition consistent.

```ruby
Fixtury.define do
namespace "use_cases" do
namespace "onboarded", isolate: true do

fixture "user" do
User.create(...)
end

fixture "profile", deps: "user" do |deps|
profile = Profile.create(user: deps.user, ...)
user.update(profiles_count: 1, onboarded_at: Time.current)
profile
end

end
end
end
```

### ActiveRecord Integration

When installed with the railtie, a MutationObserver module is prepended into ActiveRecord::Base. It observes record mutations and ensures a record is not mutated outside of the declared isolation level. If you're not using ActiveRecord check out Fixtury::MutationObserver to see how you could hook into other frameworks.

In a CI environment, we'd likely want to preload all fixtures. This can be done by requiring all the test files, then telling the fixtury store to load all definitions.
12 changes: 6 additions & 6 deletions fixtury.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_development_dependency "autotest"
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "bundler"
spec.add_development_dependency "byebug"
spec.add_development_dependency "globalid"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "minitest-reporters"
spec.add_development_dependency "activerecord"
spec.add_development_dependency "m"
spec.add_development_dependency "minitest"
spec.add_development_dependency "mocha"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "sqlite"
spec.add_development_dependency "rake"
spec.add_development_dependency "sqlite3"
end
Loading

0 comments on commit e554256

Please sign in to comment.