Skip to content

Commit 664470b

Browse files
authored
Merge pull request rails#51625 from fatkodima/assertionless-tests
Allow assertionless tests to be reported
2 parents f04c59b + 76966f9 commit 664470b

File tree

8 files changed

+165
-0
lines changed

8 files changed

+165
-0
lines changed

activesupport/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
* Allow assertionless tests to be reported.
2+
3+
`ActiveSupport::TestCase` can be configured to report tests that do not run any assertions.
4+
This is helpful in detecting broken tests that do not perform intended assertions.
5+
6+
```ruby
7+
config.active_support.assertionless_tests_behavior = :raise
8+
```
9+
10+
*fatkodima*
11+
112
* Support `hexBinary` type in `ActiveSupport::XmlMini`.
213

314
*heka1024*

activesupport/lib/active_support/railtie.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ class Railtie < Rails::Railtie # :nodoc:
124124
ActiveSupport.deprecator.warn("config.active_support.remove_deprecated_time_with_zone_name is deprecated and will be removed in Rails 7.2.")
125125
elsif k == :use_rfc4122_namespaced_uuids
126126
ActiveSupport.deprecator.warn("config.active_support.use_rfc4122_namespaced_uuids is deprecated and will be removed in Rails 7.2.")
127+
elsif k == :assertionless_tests_behavior
128+
ActiveSupport::TestCase.assertionless_tests_behavior = v
127129
else
128130
k = "#{k}="
129131
ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k

activesupport/lib/active_support/test_case.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "minitest"
44
require "active_support/testing/tagged_logging"
55
require "active_support/testing/setup_and_teardown"
6+
require "active_support/testing/assertionless_tests"
67
require "active_support/testing/assertions"
78
require "active_support/testing/error_reporter_assertions"
89
require "active_support/testing/deprecation"
@@ -142,6 +143,7 @@ def parallelize_teardown(&block)
142143

143144
include ActiveSupport::Testing::TaggedLogging
144145
prepend ActiveSupport::Testing::SetupAndTeardown
146+
prepend ActiveSupport::Testing::AssertionlessTests
145147
include ActiveSupport::Testing::Assertions
146148
include ActiveSupport::Testing::ErrorReporterAssertions
147149
include ActiveSupport::Testing::Deprecation
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveSupport
4+
module Testing
5+
# Allows to configure the behavior when the test case has no assertions in it.
6+
# This is helpful in detecting broken tests that do not perform intended assertions.
7+
module AssertionlessTests # :nodoc:
8+
def self.prepended(klass)
9+
klass.class_attribute :_assertionless_tests_behavior, instance_accessor: false, instance_predicate: false
10+
klass.extend ClassMethods
11+
klass.assertionless_tests_behavior = :ignore
12+
end
13+
14+
module ClassMethods
15+
def assertionless_tests_behavior
16+
_assertionless_tests_behavior
17+
end
18+
19+
def assertionless_tests_behavior=(behavior)
20+
self._assertionless_tests_behavior =
21+
case behavior
22+
when :ignore, nil
23+
nil
24+
when :log
25+
logger =
26+
if defined?(Rails.logger) && Rails.logger
27+
Rails.logger
28+
else
29+
require "active_support/logger"
30+
ActiveSupport::Logger.new($stderr)
31+
end
32+
33+
->(message) { logger.warn(message) }
34+
when :raise
35+
->(message) { raise Minitest::Assertion, message }
36+
when Proc
37+
behavior
38+
else
39+
raise ArgumentError, "assertionless_tests_behavior must be one of :ignore, :log, :raise, or a custom proc."
40+
end
41+
end
42+
end
43+
44+
def after_teardown
45+
super
46+
47+
return if skipped? || error?
48+
49+
if assertions == 0 && (behavior = self.class.assertionless_tests_behavior)
50+
file, line = method(name).source_location
51+
message = "Test is missing assertions: `#{name}` #{file}:#{line}"
52+
behavior.call(message)
53+
end
54+
end
55+
end
56+
end
57+
end
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../abstract_unit"
4+
require "active_support/core_ext/object/with"
5+
6+
module AssertionlessTestsTest
7+
module Tests
8+
def test_without_assertions
9+
end
10+
end
11+
12+
class AssertionlessTestsIgnore < ActiveSupport::TestCase
13+
module AfterTeardown
14+
def after_teardown
15+
ActiveSupport::TestCase.with(assertionless_tests_behavior: :ignore) do
16+
assert_nothing_raised do
17+
super
18+
end
19+
end
20+
end
21+
end
22+
23+
include Tests
24+
prepend AfterTeardown
25+
end
26+
27+
class AssertionlessTestsLog < ActiveSupport::TestCase
28+
module AfterTeardown
29+
def after_teardown
30+
_out, err = capture_io do
31+
ActiveSupport::TestCase.with(assertionless_tests_behavior: :log) do
32+
super
33+
end
34+
end
35+
assert_match(/Test is missing assertions: `test_without_assertions` .+assertionless_tests_test\.rb:\d+/, err)
36+
end
37+
end
38+
39+
include Tests
40+
prepend AfterTeardown
41+
end
42+
43+
class AssertionlessTestsRaise < ActiveSupport::TestCase
44+
module AfterTeardown
45+
def after_teardown
46+
ActiveSupport::TestCase.with(assertionless_tests_behavior: :raise) do
47+
assert_raise(Minitest::Assertion,
48+
match: /Test is missing assertions: `test_without_assertions` .+assertionless_tests_test\.rb:\d+/) do
49+
super
50+
end
51+
end
52+
end
53+
end
54+
55+
include Tests
56+
prepend AfterTeardown
57+
end
58+
59+
class AssertionlessTestsUnknown < ActiveSupport::TestCase
60+
def test_raises_when_unknown_behavior
61+
assert_raises(ArgumentError, match: /assertionless_tests_behavior must be one of/) do
62+
ActiveSupport::TestCase.assertionless_tests_behavior = :unknown
63+
end
64+
end
65+
end
66+
end

guides/source/configuring.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Below are the default values associated with each target version. In cases of co
6464
- [`config.active_record.automatically_invert_plural_associations`](#config-active-record-automatically-invert-plural-associations): `true`
6565
- [`config.active_record.validate_migration_timestamps`](#config-active-record-validate-migration-timestamps): `true`
6666
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w[image/png image/jpeg image/gif image/webp]`
67+
- [`config.active_support.assertionless_tests_behavior`](#config-active-support-assertionless-tests-behavior): `:log`
6768

6869
#### Default Values for Target Version 7.1
6970

@@ -2656,6 +2657,18 @@ The default value depends on the `config.load_defaults` target version:
26562657
| (original) | `false` |
26572658
| 7.0 | `true` |
26582659

2660+
#### `config.active_support.assertionless_tests_behavior`
2661+
2662+
Controls the behavior when a test do not run any assertions. The following options are available:
2663+
2664+
* `:ignore` - Assertionless tests will be ignored. This is the default.
2665+
2666+
* `:log` - Assertionless tests will be logged via `Rails.logger` at the `:warn` level.
2667+
2668+
* `:raise` - Assertionless tests will be considered as failed and raise an error.
2669+
2670+
* Custom proc - A custom proc can be provided. It should accept an error message.
2671+
26592672
#### `ActiveSupport::Logger.silencer`
26602673

26612674
Is set to `false` to disable the ability to silence logging in a block. The default is `true`.

railties/lib/rails/application/configuration.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,12 @@ def load_defaults(target_version)
334334
active_record.validate_migration_timestamps = true
335335
active_record.automatically_invert_plural_associations = true
336336
end
337+
338+
if respond_to?(:active_support)
339+
if Rails.env.local?
340+
active_support.assertionless_tests_behavior = :log
341+
end
342+
end
337343
else
338344
raise "Unknown version #{target_version.to_s.inspect}"
339345
end

railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_2.rb.tt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,11 @@
6666
# on `Post`. With this option enabled, it will also look for a `:comments` association.
6767
#++
6868
# Rails.application.config.active_record.automatically_invert_plural_associations = true
69+
70+
###
71+
# Controls whether Active Support should log tests without assertions.
72+
# This is helpful in detecting broken tests that do not perform intended assertions.
73+
# To disable this behavior, set the value to `:ignore` inside the `config/application.rb` (NOT this file).
74+
#
75+
#++
76+
# Rails.application.config.active_support.assertionless_tests_behavior = :log

0 commit comments

Comments
 (0)