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

Feature: Add Rails generator and rake task support for creating data migration test coverage #355

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

joshmfrankel
Copy link

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

  • Adds new Rake task rake data:tests:setup for optional help setting up new data migration tests
  • Adds new Config setting config.test_support_enabled which conditionally creates associated tests files upon rails g data_migration add_this_to_that. (Default: false)
  • Adds basic support for inferring test suite based on available helper file

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 files test/test_helper.rb and spec/rails_helper.rb.

Resources

Also, kudos on a great gem! 💎

…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'))
Copy link
Author

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.

Copy link
Collaborator

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["|']/)
Copy link
Author

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')
Copy link
Author

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?

Copy link
Collaborator

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 }"
Copy link
Collaborator

Choose a reason for hiding this comment

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

https://github.com/vprigent/data-migrate/blob/517328051687873c9f8412efe2d6a8f2cf7e9157/README.md?plain=1#L167

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:

Suggested change
"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 }"

Copy link
Collaborator

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')
Copy link
Collaborator

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")
Copy link
Collaborator

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.

Copy link
Collaborator

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
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants