diff --git a/changelog/new_rails_controller_testing_cop.md b/changelog/new_rails_controller_testing_cop.md new file mode 100644 index 0000000000..90717d0dcf --- /dev/null +++ b/changelog/new_rails_controller_testing_cop.md @@ -0,0 +1 @@ +* [#638](https://github.com/rubocop/rubocop-rails/pull/614): Add new `Rails/ControllerTesting` cop. ([@gmcgibbon][]) diff --git a/config/default.yml b/config/default.yml index 446672b277..5803a96de6 100644 --- a/config/default.yml +++ b/config/default.yml @@ -199,6 +199,16 @@ Rails/ContentTag: Exclude: - app/models/**/*.rb +Rails/ControllerTesting: + Description: 'Use `ActionDispatch::IntegrationTest` instead of `ActionController::TestCase`.' + Reference: + - 'https://api.rubyonrails.org/classes/ActionController/TestCase.html' + Enabled: true + VersionAdded: '<>' + Include: + - test/**/*.rb + + Rails/CreateTableWithTimestamps: Description: >- Checks the migration for which timestamps are not included diff --git a/lib/rubocop/cop/rails/controller_testing.rb b/lib/rubocop/cop/rails/controller_testing.rb new file mode 100644 index 0000000000..ea7cd34554 --- /dev/null +++ b/lib/rubocop/cop/rails/controller_testing.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Rails + # Using ActionController::TestCase is discouraged and should be replaced by + # ActionDispatch::IntegrationTest. Controller tests are too close to the + # internals of a controller whereas integration tests mimick the browser/user. + # + # @safety + # This cop's autocorrection is unsafe because the API of each test case class is different. + # Make sure to update each test of your controller test cases after changing the superclass. + # + # @example + # # bad + # class MyControllerTest < ActionController::TestCase + # end + # + # # good + # class MyControllerTest < ActionDispatch::IntegrationTest + # end + # + class ControllerTesting < Base + extend AutoCorrector + extend TargetRailsVersion + + MSG = 'Use ActionDispatch::IntegrationTest instead. See https://api.rubyonrails.org/classes/ActionController/TestCase.html' + + minimum_target_rails_version 5.0 + + def_node_matcher :controller_test_case?, <<~PATTERN + (class + (const nil? _) + (const (const nil? :ActionController) :TestCase) nil?) + PATTERN + + def on_class(node) + return unless controller_test_case?(node) + + add_offense(node.parent_class) do |corrector| + corrector.replace(node.parent_class, 'ActionDispatch::IntegrationTest') + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rails_cops.rb b/lib/rubocop/cop/rails_cops.rb index 31404629a9..b8323c65dc 100644 --- a/lib/rubocop/cop/rails_cops.rb +++ b/lib/rubocop/cop/rails_cops.rb @@ -26,6 +26,7 @@ require_relative 'rails/bulk_change_table' require_relative 'rails/compact_blank' require_relative 'rails/content_tag' +require_relative 'rails/controller_testing' require_relative 'rails/create_table_with_timestamps' require_relative 'rails/date' require_relative 'rails/default_scope' diff --git a/spec/rubocop/cop/rails/controller_testing_spec.rb b/spec/rubocop/cop/rails/controller_testing_spec.rb new file mode 100644 index 0000000000..cf9360890a --- /dev/null +++ b/spec/rubocop/cop/rails/controller_testing_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Rails::ControllerTesting, :config do + context 'Rails 4.2', :rails42, :config do + it 'does not add offense when extending ActionController::TestCase' do + expect_no_offenses(<<~RUBY) + class MyControllerTest < ActionController::TestCase + end + RUBY + end + end + + it 'adds offense when extending ActionController::TestCase' do + expect_offense(<<~RUBY) + class MyControllerTest < ActionController::TestCase + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use ActionDispatch::IntegrationTest instead. See https://api.rubyonrails.org/classes/ActionController/TestCase.html + end + RUBY + + expect_correction(<<~RUBY) + class MyControllerTest < ActionDispatch::IntegrationTest + end + RUBY + end + + it 'does not add offense when extending ActionDispatch::IntegrationTest' do + expect_no_offenses(<<~RUBY) + class MyControllerTest < ActionDispatch::IntegrationTest; end + RUBY + end +end