Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up DatabaseCleaner for tests #28

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion lib/hanami/rspec/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def call(*, **)
copy_dotrspec
copy_spec_helper
copy_support_rspec
copy_support_db
copy_support_features
copy_support_operations
copy_support_requests
Expand All @@ -28,9 +29,11 @@ def call(*, **)
private

def append_gemfile
gemfile_template = Hanami.bundled?("hanami-db") ? "gemfile_db" : "gemfile"

fs.append(
fs.expand_path("Gemfile"),
fs.read(fs.expand_path(fs.join("generators", "gemfile"), __dir__))
fs.read(fs.expand_path(fs.join("generators", gemfile_template), __dir__))
)
end

Expand Down Expand Up @@ -62,6 +65,20 @@ def copy_support_rspec
)
end

def copy_support_db
return unless Hanami.bundled?("hanami-db")

fs.cp(
fs.expand_path(fs.join("generators/support_db.rb"), __dir__),
fs.expand_path(fs.join("spec", "support", "db.rb"))
)

fs.cp(
fs.expand_path(fs.join("generators/support_db_cleaning.rb"), __dir__),
fs.expand_path(fs.join("spec", "support", "db", "cleaning.rb"))
)
end

def copy_support_features
fs.cp(
fs.expand_path(fs.join("generators", "support_features.rb"), __dir__),
Expand Down
1 change: 1 addition & 0 deletions lib/hanami/rspec/generators/gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

group :test do
# Web integration
gem "capybara"
gem "rack-test"
end
9 changes: 9 additions & 0 deletions lib/hanami/rspec/generators/gemfile_db
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

group :test do
# Database
gem "database_cleaner-sequel"

# Web integration
gem "capybara"
gem "rack-test"
end
10 changes: 10 additions & 0 deletions lib/hanami/rspec/generators/support_db.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

# Tag feature spec examples as `:db`
#
# See support/db/cleaning.rb for how the database is cleaned around these `:db` examples.
RSpec.configure do |config|
config.define_derived_metadata(type: :feature) do |metadata|
metadata[:db] = true
end
end
42 changes: 42 additions & 0 deletions lib/hanami/rspec/generators/support_db_cleaning.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

require "database_cleaner/sequel"

# Clean the databases between tests tagged as `:db`
RSpec.configure do |config|
# Returns all the configured databases across the app and its slices.
#
# Used in the before/after hooks below to ensure each database is cleaned between examples.
#
# Modify this proc (or any code below) if you only need specific databases cleaned.
all_databases = -> {
slices = [Hanami.app] + Hanami.app.slices.with_nested

slices.each_with_object([]) { |slice, dbs|
next unless slice.key?("db.rom")

dbs.concat slice["db.rom"].gateways.values.map(&:connection)
}.uniq
}
Comment on lines +12 to +20
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this a proc because if I instead made it a method, then I'd need a place to put it. In my own apps I use module Test (and various nested modules within it) to put utility code like this, but for standard Hanami apps, I don't think we're yet at the point where we can introduce a structure like that. I'd like to do it, but it will need a bit more thought on the approach, as well as additional effort put into docs, both of which too much to fit in our remaining time before 2.2.

And so we have the proc. It remains local, so no concrete method naming required, and given the size of this file, I think it's still readable enough.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with your caution. While RSpec is clearly the default in the community, it would be good to be forward-thinking and allow something new and interesting to be used in addition to just minitest, and that flexibility will be hard.

What concerns me about RSpec is how difficult parallelism is with it, which contributes to long-term slow test suites in my experience. The modular separation of Slices in Hanami would otherwise make parallel testing very attractive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks for considering this, @alassek. Glad to hear you agree.

(I'd love for us to provide a parallel testing experience in the future!)


config.before :suite do
all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].clean_with :truncation, except: ["schema_migrations"]
end
end

config.before :each, :db do |example|
strategy = example.metadata[:js] ? :truncation : :transaction

all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].strategy = strategy
DatabaseCleaner[:sequel, db: db].start
end
end

config.after :each, :db do
all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].clean
end
end
end
96 changes: 96 additions & 0 deletions spec/unit/hanami/rspec/commands/install_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

let(:arbitrary_argument) { {} }

before do
allow(Hanami).to receive(:bundled?).and_call_original
allow(Hanami).to receive(:bundled?).with("hanami-db").and_return true
end

around do |example|
fs.chdir(dir) { example.run }
ensure
Expand All @@ -25,6 +30,10 @@
# Gemfile
gemfile = <<~EOF
group :test do
# Database
gem "database_cleaner-sequel"
# Web integration
gem "capybara"
gem "rack-test"
end
Expand Down Expand Up @@ -123,6 +132,66 @@
EOF
expect(fs.read("spec/support/rspec.rb")).to eq(support_rspec)

support_db = <<~EOF
# frozen_string_literal: true
# Tag feature spec examples as `:db`
#
# See support/db/cleaning.rb for how the database is cleaned around these `:db` examples.
RSpec.configure do |config|
config.define_derived_metadata(type: :feature) do |metadata|
metadata[:db] = true
end
end
EOF
expect(fs.read("spec/support/db.rb")).to eq support_db

support_db_cleaning = <<~EOF
# frozen_string_literal: true
require "database_cleaner/sequel"
# Clean the databases between tests tagged as `:db`
RSpec.configure do |config|
# Returns all the configured databases across the app and its slices.
#
# Used in the before/after hooks below to ensure each database is cleaned between examples.
#
# Modify this proc (or any code below) if you only need specific databases cleaned.
all_databases = -> {
slices = [Hanami.app] + Hanami.app.slices.with_nested
slices.each_with_object([]) { |slice, dbs|
next unless slice.key?("db.rom")
dbs.concat slice["db.rom"].gateways.values.map(&:connection)
}.uniq
}
config.before :suite do
all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].clean_with :truncation, except: ["schema_migrations"]
end
end
config.before :each, :db do |example|
strategy = example.metadata[:js] ? :truncation : :transaction
all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].strategy = strategy
DatabaseCleaner[:sequel, db: db].start
end
end
config.after :each, :db do
all_databases.call.each do |db|
DatabaseCleaner[:sequel, db: db].clean
end
end
end
EOF
expect(fs.read("spec/support/db/cleaning.rb")).to eq support_db_cleaning

# spec/support/features.rb
support_features = <<~EOF
# frozen_string_literal: true
Expand Down Expand Up @@ -179,5 +248,32 @@
EOF
expect(fs.read("spec/requests/root_spec.rb")).to eq(request_spec)
end

context "hanami-db not bundled" do
before do
allow(Hanami).to receive(:bundled?).with("hanami-db").and_return false
end

it "does not add the database_cleaner gem to the Gemfile" do
subject.call(arbitrary_argument)

# Gemfile
gemfile = <<~EOF
group :test do
# Web integration
gem "capybara"
gem "rack-test"
end
EOF
expect(fs.read("Gemfile")).to include(gemfile)
end

it "does not add DB-related spec support files" do
subject.call(arbitrary_argument)

expect(fs.exist?("spec/support/db.rb")).to be false
expect(fs.exist?("spec/support/db/cleaning.rb")).to be false
end
end
end
end