Skip to content

Commit c7df5b1

Browse files
committed
Add fail_if_no_examples option
Exit with a failure status if no RSpec examples. Default behavior not changed. CLI option not implemented.
1 parent f9177cc commit c7df5b1

File tree

7 files changed

+168
-3
lines changed

7 files changed

+168
-3
lines changed

features/.nav

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- read_options_from_file.feature
4444
- color.feature
4545
- fail_fast.feature
46+
- fail_if_no_examples.feature
4647
- custom_settings.feature
4748
- alias_example_to.feature
4849
- default_path.feature
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Feature: fail if no examples
2+
3+
Use the `fail_if_no_examples` option to make RSpec exit with a failure status (by default 1) if there are no examples. Using this option, it is recommended to add a `--require spec_helper` option to `.rspec` file to ensure the `fail_if_no_examples` option is set even if no spec files are loaded.
4+
5+
This option may be particularly useful when you happen to not run RSpec tests locally, but rely on CI to do this. This prevents from false positive builds, when you expected some RSpec examples to be run, but none were run. Such a situation may be caused by your misconfiguration or regression/major changes in RSpec.
6+
7+
Background:
8+
Given a file named "spec/spec_helper.rb" with:
9+
"""ruby
10+
RSpec.configure { |c| c.fail_if_no_examples = true }
11+
"""
12+
Given a file named ".rspec" with:
13+
"""ruby
14+
--require spec_helper
15+
"""
16+
Given a file named "spec/some.spec.rb" with:
17+
"""ruby
18+
RSpec.describe 'something' do
19+
it 'succeeds' do
20+
true
21+
end
22+
end
23+
"""
24+
25+
Scenario: Examples file name is not matched by RSpec pattern, thus there are no examples run
26+
When I run `rspec`
27+
Then it should fail with "0 examples, 0 failures"
28+
29+
Scenario: Examples file name is matched by RSpec pattern, 1 example is run
30+
When I run `rspec --pattern spec/**/*.spec.rb`
31+
Then it should pass with "1 example, 0 failures"

lib/rspec/core/configuration.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ def only_failures_but_not_configured?
199199
# The exit code to return if there are any failures (default: 1).
200200
add_setting :failure_exit_code
201201

202+
# @macro add_setting
203+
# Whether or not to fail when there are no RSpec examples (default: false).
204+
add_setting :fail_if_no_examples
205+
202206
# @macro define_reader
203207
# Indicates files configured to be required.
204208
define_reader :requires
@@ -425,6 +429,7 @@ def initialize
425429
@pattern = '**{,/*/**}/*_spec.rb'
426430
@exclude_pattern = ''
427431
@failure_exit_code = 1
432+
@fail_if_no_examples = false
428433
@spec_files_loaded = false
429434

430435
@backtrace_formatter = BacktraceFormatter.new

lib/rspec/core/runner.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,12 @@ def setup(err, out)
108108
# or the configured failure exit code (1 by default) if specs
109109
# failed.
110110
def run_specs(example_groups)
111-
@configuration.reporter.report(@world.example_count(example_groups)) do |reporter|
111+
examples_count = @world.example_count(example_groups)
112+
@configuration.reporter.report(examples_count) do |reporter|
112113
@configuration.with_suite_hooks do
114+
if examples_count == 0 && @configuration.fail_if_no_examples
115+
return @configuration.failure_exit_code
116+
end
113117
example_groups.map { |g| g.run(reporter) }.all? ? 0 : @configuration.failure_exit_code
114118
end
115119
end
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
require 'support/aruba_support'
2+
3+
RSpec.describe 'Fail if no examples' do
4+
include_context "aruba support"
5+
before { clean_current_dir }
6+
7+
context 'when 1 passing example' do
8+
def passing_example(fail_if_no_examples)
9+
"
10+
RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} }
11+
12+
RSpec.describe 'something' do
13+
it 'succeeds' do
14+
true
15+
end
16+
end
17+
"
18+
end
19+
20+
it 'succeeds if fail_if_no_examples set to true' do
21+
write_file 'spec/example_spec.rb', passing_example(true)
22+
run_command ""
23+
expect(last_cmd_stdout).to include("1 example, 0 failures")
24+
expect(last_cmd_exit_status).to eq(0)
25+
end
26+
27+
it 'succeeds if fail_if_no_examples set to false' do
28+
write_file 'spec/example_spec.rb', passing_example(false)
29+
run_command ""
30+
expect(last_cmd_stdout).to include("1 example, 0 failures")
31+
expect(last_cmd_exit_status).to eq(0)
32+
end
33+
end
34+
35+
context 'when 1 failing example' do
36+
def failing_example(fail_if_no_examples)
37+
"
38+
RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} }
39+
40+
RSpec.describe 'something' do
41+
it 'fails' do
42+
fail
43+
end
44+
end
45+
"
46+
end
47+
48+
it 'fails if fail_if_no_examples set to true' do
49+
write_file 'spec/example_spec.rb', failing_example(true)
50+
run_command ""
51+
expect(last_cmd_stdout).to include("1 example, 1 failure")
52+
expect(last_cmd_exit_status).to eq(1)
53+
end
54+
55+
it 'fails if fail_if_no_examples set to false' do
56+
write_file 'spec/example_spec.rb', failing_example(false)
57+
run_command ""
58+
expect(last_cmd_stdout).to include("1 example, 1 failure")
59+
expect(last_cmd_exit_status).to eq(1)
60+
end
61+
end
62+
63+
context 'when 0 examples' do
64+
def no_examples(fail_if_no_examples)
65+
"
66+
RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} }
67+
68+
RSpec.describe 'something' do
69+
end
70+
"
71+
end
72+
73+
it 'fails if fail_if_no_examples set to true' do
74+
write_file 'spec/example_spec.rb', no_examples(true)
75+
run_command ""
76+
expect(last_cmd_stdout).to include("0 examples, 0 failures")
77+
expect(last_cmd_exit_status).to eq(1)
78+
end
79+
80+
it 'succeeds if fail_if_no_examples set to false' do
81+
write_file 'spec/example_spec.rb', no_examples(false)
82+
run_command ""
83+
expect(last_cmd_stdout).to include("0 examples, 0 failures")
84+
expect(last_cmd_exit_status).to eq(0)
85+
end
86+
87+
context 'when custom failure_exit_code set' do
88+
def no_examples_custom_failure_exit_code(fail_if_no_examples)
89+
"
90+
RSpec.configure do |c|
91+
c.fail_if_no_examples = #{fail_if_no_examples}
92+
c.failure_exit_code = 15
93+
end
94+
95+
RSpec.describe 'something' do
96+
end
97+
"
98+
end
99+
100+
it 'fails if fail_if_no_examples set to true' do
101+
write_file 'spec/example_spec.rb', no_examples_custom_failure_exit_code(true)
102+
run_command ""
103+
expect(last_cmd_stdout).to include("0 examples, 0 failures")
104+
expect(last_cmd_exit_status).to eq(15)
105+
end
106+
end
107+
end
108+
end

spec/rspec/core/configuration_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ module RSpec::Core
4242
end
4343
end
4444

45+
describe 'fail_if_no_examples' do
46+
it 'defaults to false' do
47+
expect(RSpec::Core::Configuration.new.fail_if_no_examples).to be(false)
48+
end
49+
50+
it 'can be set to true' do
51+
config.fail_if_no_examples = true
52+
expect(config.fail_if_no_examples).to eq(true)
53+
end
54+
55+
it 'can be set to false' do
56+
config.fail_if_no_examples = false
57+
expect(config.fail_if_no_examples).to eq(false)
58+
end
59+
end
60+
4561
describe '#deprecation_stream' do
4662
it 'defaults to standard error' do
4763
expect($rspec_core_without_stderr_monkey_patch.deprecation_stream).to eq STDERR

spec/support/aruba_support.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module ArubaLoader
1010
let(:stderr) { StringIO.new }
1111
let(:stdout) { StringIO.new }
1212

13-
attr_reader :last_cmd_stdout, :last_cmd_stderr
13+
attr_reader :last_cmd_stdout, :last_cmd_stderr, :last_cmd_exit_status
1414

1515
def run_command(cmd)
1616
RSpec.configuration.color = true
@@ -24,7 +24,7 @@ def run_command(cmd)
2424

2525
handle_current_dir_change do
2626
in_current_dir do
27-
RSpec::Core::Runner.run(cmd_parts, temp_stderr, temp_stdout)
27+
@last_cmd_exit_status = RSpec::Core::Runner.run(cmd_parts, temp_stderr, temp_stdout)
2828
end
2929
end
3030
ensure

0 commit comments

Comments
 (0)