-
Notifications
You must be signed in to change notification settings - Fork 196
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
Feature: Add Rails generator and rake task support for creating data migration test coverage #355
base: main
Are you sure you want to change the base?
Conversation
…ge on data migrations * New Rake task `rake data:tests:setup` * New Config setting `config.test_support = true/false` (false by default)
module Helpers | ||
class InferTestSuiteType | ||
def call | ||
if File.exist?(Rails.root.join('spec', 'spec_helper.rb')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could perform add to the conditionals here for Rails.configuration.generators.options[:rails][:test_framework] == :rspec
and Rails.configuration.generators.options[:rails][:test_framework] == :test_unit
respectively to look for both installation and configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of getting the test framework from Rails's config. I've worked on projects where both rspec and minitest were used (migration happening but not over and very little bandwidth to finish it) and I think its easier for us to maintain compatibility with the configuration over the presence of files?
Perhaps adding a config line would work too, but I'd imagine projects running on sinatra for example to maybe not follow the conventions set elsewhere?
module DataMigrate | ||
module Tasks | ||
class SetupTests | ||
INJECTION_MATCHER = Regexp.new(/require_relative ["|']\.\.\/config\/environment["|']/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had mixed feeling on this. Basically, I want to find a target phrase in either the rails_helper.rb or test_helper.rb. It seemed best to inject the require loop https://github.com/ilyakatz/data-migrate/pull/355/files#diff-ffd91a3de47aa2b12e05f99c726f74f27c7181b3f2f987c24aa9af32f72ea2acR57 after the environment was loaded. Open to other suggestions.
The setup task is also optional. You could just individually require files per data migration test file.
def test_helper_file_path | ||
case DataMigrate::Helpers::InferTestSuiteType.new.call | ||
when :rspec | ||
Rails.root.join('spec', 'rails_helper.rb') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rails_helper.rb
contains the injection target BUT DataMigrate::Helpers::InferTestSuiteType checks spec_helper.rb
(see: https://github.com/ilyakatz/data-migrate/pull/355/files#diff-e3eb6de888eae80ac87faa33be0419ff8abdc2bd7d14e23c243304834d53604eR5). I'm thinking these maybe should match in their conditional checks. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you were to remove the lines added to inject the require statement, this all disappears. It wouldn't be as full-fledged as this solution but avoids a bunch of complexity which users can just replace if they wished to do so.
A question that just came to mind, why not just inject the require statement inside the spec file generated?
def lines_for_injection | ||
[ | ||
"# data_migrate: Include data migrations for writing test coverage", | ||
"Dir[Rails.root.join(DataMigrate.config.data_migrations_path, '*.rb')].each { |f| require f }" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DataMigrate.config.data_migrations_path
can be an array of locations in order to support engine-based data migrations...
We could reverse this logic and do something like this perhaps:
"Dir[Rails.root.join(DataMigrate.config.data_migrations_path, '*.rb')].each { |f| require f }" | |
"Dir[*Array.wrap(DataMigrate.config.data_migrations_path).map{|path| Rails.root.join(path, '*.rb')}].each { |f| require f }" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly think it would be easier to let the user require whichever file they are about to test within their spec file though?
def test_helper_file_path | ||
case DataMigrate::Helpers::InferTestSuiteType.new.call | ||
when :rspec | ||
Rails.root.join('spec', 'rails_helper.rb') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you were to remove the lines added to inject the require statement, this all disappears. It wouldn't be as full-fledged as this solution but avoids a bunch of complexity which users can just replace if they wished to do so.
A question that just came to mind, why not just inject the require statement inside the spec file generated?
end | ||
|
||
def data_migrations_spec_file_path | ||
File.join(Rails.root, 'spec', DataMigrate.config.data_migrations_path, "#{file_name}_spec.rb") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may have issues here too with data_migrations_path not being necessarily a single path either here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use the same logic here:
https://github.com/vprigent/data-migrate/blob/517328051687873c9f8412efe2d6a8f2cf7e9157/lib/generators/data_migration/data_migration_generator.rb#L61-L64
Though ideally we'd abstract that away and make a definitive location as being the location for new migrations while still allowing engines/libraries to integrate with data_migration in a way that's painless for users.
subject.create_data_migration | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these specs seem to create the data migration file here, but doesn't clean it up from the local repo once specs are done running.
when test support is enabled
and the test suite is RSpec
identical db/data/20241225214448_my_migration.rb
creates a spec file
and the test suite is Minitest
identical db/data/20241225214448_my_migration.rb
I'd guess this is coming from a part of the code you didn't mean to test in the first place. I wonder if there's a clean post suite hook we could write to remove any file actually created as part of the specs.
Why
Implements: #148
I recently needed well-factored test coverage for a complex data migration, so I thought I'd take an attempt at implementing test suite support for both RSpec and Minitest.
What
rake data:tests:setup
for optional help setting up new data migration testsconfig.test_support_enabled
which conditionally creates associated tests files uponrails g data_migration add_this_to_that
. (Default: false)I read the comment here: #162 (comment) regarding using
Rails.configuration.generators.options[:rails]
. That returns a very clear:Rails.configuration.generators.options[:rails][:test_framework] #=> :rspec
. While testing Minitest and RSpec, I noticed that this comes from having the rspec-rails gem installed. It doesn't necessarily check for valid installation. I attempted a valid installation check by looking for the framework specific filestest/test_helper.rb
andspec/rails_helper.rb
.Resources
Also, kudos on a great gem! 💎