Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

100% test coverage #1458

Merged
merged 17 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/rspec/expectations/block_snippet_extractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ def source
RSpec.world.source_from_file(file_path)
end
else
# :nocov:
RSpec::Support.require_rspec_support 'source'
def source
raise TargetNotFoundError unless File.exist?(file_path)
@source ||= RSpec::Support::Source.from_file(file_path)
end
# :nocov:
end

def file_path
Expand Down
5 changes: 5 additions & 0 deletions lib/rspec/expectations/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def color?
::RSpec.configuration.color_enabled?
end
else
# :nocov:
# Indicates whether or not diffs should be colored.
# Delegates to rspec-core's color option if rspec-core
# is loaded; otherwise you can set it here.
Expand All @@ -100,8 +101,11 @@ def color?
def color?
defined?(@color) && @color
end
# :nocov:
end

# :nocov: Because this is only really _useful_ on 1.8, and hard to test elsewhere.
#
# Adds `should` and `should_not` to the given classes
# or modules. This can be used to ensure `should` works
# properly on things like proxy objects (particular
Expand All @@ -114,6 +118,7 @@ def add_should_and_should_not_to(*modules)
Expectations::Syntax.enable_should(mod)
end
end
# :nocov:

# Sets or gets the backtrace formatter. The backtrace formatter should
# implement `#format_backtrace(Array<String>)`. This is used
Expand Down
7 changes: 7 additions & 0 deletions lib/rspec/expectations/failure_aggregator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ class FailureAggregator

# @private
class AggregatedFailure
# :nocov:
# `inspect` was apparently used by some versions early in ruby 3 while constructing
# NoMethodError, but seems to be no longer.
#
# @private
MESSAGE =
'AggregatedFailure: This method caused a failure which has been ' \
Expand All @@ -15,6 +19,7 @@ class AggregatedFailure
def inspect
MESSAGE
end
# :nocov:
end

AGGREGATED_FAILURE = AggregatedFailure.new
Expand Down Expand Up @@ -75,11 +80,13 @@ def call(failure, options)
# a backtrace that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
# so that rspec-core's truncation logic can work properly on it to list the backtrace
# relative to the `aggregate_failures` block.
# :nocov:
def assign_backtrace(failure)
raise failure
rescue failure.class => e
failure.set_backtrace(e.backtrace)
end
# :nocov:
else
# Using `caller` performs better (and is simpler) than `raise` on most Rubies.
def assign_backtrace(failure)
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ class << self
# than _before_, like `append_features`. It's important we check this before
# in order to find the cases where it was already previously included.
# @api private
# :nocov:
def append_features(mod)
return super if mod < self # `mod < self` indicates a re-inclusion.

Expand All @@ -1038,6 +1039,7 @@ def append_features(mod)

super
end
# :nocov:
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rspec/matchers/built_in/base_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ def string_encoding_differs?
else
# @api private
# @return [Boolean] False always as the curent Ruby version does not support String encoding
# :nocov:
def string_encoding_differs?
false
end
# :nocov:
end
module_function :string_encoding_differs?

Expand All @@ -175,9 +177,11 @@ def format_encoding(value)
# Formats a String's encoding as a human readable string
# @param _value [String]
# @return [nil] nil as the curent Ruby version does not support String encoding
# :nocov:
def format_encoding(_value)
nil
end
# :nocov:
end
module_function :format_encoding
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/change.rb
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,11 @@ def extract_value_block_snippet
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
end
else
# :nocov:
def extract_value_block_snippet
nil
end
# :nocov:
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/contain_exactly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ def safe_sort(array)
end

if RUBY_VERSION == "1.8.7"
# :nocov:
def to_a_disallowed?(object)
case object
when NilClass, String then true
else Kernel == RSpec::Support.method_handle_for(object, :to_a).owner
end
end
# :nocov:
else
def to_a_disallowed?(object)
NilClass === object
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/count_expectation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ def cover?(count, number)
count.cover?(number)
end
else
# :nocov:
def cover?(count, number)
number >= count.first && number <= count.last
end
# :nocov:
end

def expected_count_matches?(actual_count)
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/has.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ def really_responds_to?(method)
end
end
else
# :nocov:
def really_responds_to?(method)
@actual.respond_to?(method)
end
# :nocov:
end

def predicate_accessible?
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/include.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,11 @@ def actual_collection_includes?(expected_item)
end

if RUBY_VERSION < '1.9'
# :nocov:
def count_enumerable(expected_item)
actual.select { |value| values_match?(expected_item, value) }.size
end
# :nocov:
else
def count_enumerable(expected_item)
actual.count { |value| values_match?(expected_item, value) }
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/match.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ def initialize(match_data)
# @api private
# Returns match data names for named captures
# @return Array
# :nocov:
def names
[]
end
# :nocov:
else
# @api private
# Returns match data names for named captures
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/built_in/satisfy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ def extract_block_snippet
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@block, matcher_name)
end
else
# :nocov:
def block_representation
'block'
end
# :nocov:
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rspec/matchers/english_phrasing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ def self.list(obj)
# So here we replace `Kernel#Array` with our own warning-free implementation for 1.8.7.
# @private
# rubocop:disable Naming/MethodName
# :nocov:
def self.Array(obj)
case obj
when Array then obj
else [obj]
end
end
# :nocov:
# rubocop:enable Naming/MethodName
end
end
Expand Down
6 changes: 6 additions & 0 deletions spec/rspec/expectations/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ def delegated?; true; end
config.on_potential_false_positives = :raise
expect(config.on_potential_false_positives).to eq :raise
end

it 'cannot be set to :fooba' do
expect {
config.on_potential_false_positives = :fooba
}.to raise_error(ArgumentError, /Supported values are/)
end
end

shared_examples "configuring the expectation syntax" do
Expand Down
27 changes: 13 additions & 14 deletions spec/rspec/expectations/failure_aggregator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -273,20 +273,19 @@ def expect_error_included_in_aggregated_failure(error)
array = dbl.get(2)
array << :foo
end
}.to raise_error(
be_a(MultipleExpectationsNotMetError).and have_attributes(
:failures => [
be_a(RSpec::Mocks::MockExpectationError)
],
:other_errors => [
# Checking only the class name, not the full message, because the hehaviour differes bettern Ruby 2 and 3.
# Ruby 3 uses #inspect in NoMethodError, while Ruby 2.x uses format that I can't override:
# NoMethodError (undefined method `<<' for #<RSpec::Expectations::FailureAggregator::AggregatedFailure:0x000055e4bac69ba8>)
# even if I override #to_s
be_a(NoMethodError).and(have_attributes(:message => /AggregatedFailure/))
]
)
)
}.to raise_error do |err|
expect(err).to be_a(MultipleExpectationsNotMetError)
expect(err.failures).to contain_exactly(be_a(RSpec::Mocks::MockExpectationError))
expect(err.other_errors).to contain_exactly(be_a(NoMethodError))

# Checking only the class name, not the full message, because the behaviour across ruby versions.
# Some versions of Ruby 3 use #inspect in NoMethodError, but Ruby 2.x and 3.3 use formats that we
# can't control:
#
# Ruby2: undefined method `<<' for #<RSpec::Expectations::FailureAggregator::AggregatedFailure:0x000055e4bac69ba8>
# Ruby3: undefined method `<<' for an instance of RSpec::Expectations::FailureAggregator::AggregatedFailure
expect(err.other_errors.first.message).to match(/AggregatedFailure/)
end
end
end

Expand Down
6 changes: 6 additions & 0 deletions spec/rspec/matchers/built_in/all_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ module RSpec::Matchers::BuiltIn
end
end

it "refuses to invert" do
expect {
expect([5, 3, 3]).not_to all(be_odd)
}.to raise_error(NotImplementedError, /not_to all.*is not supported/)
end

context 'when single matcher is given' do

describe 'expect(...).to all(expected)' do
Expand Down
32 changes: 32 additions & 0 deletions spec/rspec/matchers/built_in/be_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,22 @@ def mouth2.frowns?; yield; end
end
end

RSpec.describe "expect(...).not_to be_truthy" do
it "passes when actual equal?(false)" do
expect(false).not_to be_truthy
end

it "passes when actual equal?(nil)" do
expect(nil).not_to be_truthy
end

it "fails when actual equal?(true)" do
expect {
expect(true).not_to be_truthy
}.to fail_with("expected: falsey value\n got: true")
end
end

RSpec.describe "expect(...).to be_falsey" do
it "passes when actual equal?(false)" do
expect(false).to be_falsey
Expand Down Expand Up @@ -556,6 +572,22 @@ def mouth2.frowns?; yield; end
end
end

RSpec.describe "expect(...).not_to be_falsey" do
it "passes when actual equal?(true)" do
expect(true).not_to be_falsey
end

it "passes when actual is 1" do
expect(1).not_to be_falsey
end

it "fails when actual equal?(false)" do
expect {
expect(false).not_to be_falsey
}.to fail_with("expected: truthy value\n got: false")
end
end

RSpec.describe "expect(...).to be_nil" do
it "passes when actual is nil" do
expect(nil).to be_nil
Expand Down
45 changes: 45 additions & 0 deletions spec/rspec/matchers/built_in/compound_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,26 @@ def expect_block
end
end

describe "expect(...).to custom_matcher.and(other_matcher)" do
it "treats a matcher that doesn't support value expectations correctly" do
expect {
expect([1, 2]).to include(1).and raise_error(/test/)
}.to fail_with(/but was not given a block/)
end

it "treats a matcher that does support value expectations correctly" do
expect([1, 2]).to include(1).and include(2)
end

it "treats a matcher that doesn't _specify_ as supporting value expectations" do
unsupporting_includes_class = Class.new(Object) do
def matches?(actual); actual.include?(2); end
end
unsupporting_includes = unsupporting_includes_class.new
expect([1, 2]).to include(1).and unsupporting_includes
end
end

describe "expect(...).to matcher.and(other_matcher)" do

it_behaves_like "an RSpec value matcher", :valid_value => 3, :invalid_value => 4, :disallows_negation => true do
Expand Down Expand Up @@ -973,5 +993,30 @@ def actual
}.to raise_error(NotImplementedError, /matcher.or matcher` is not supported/)
end
end

describe "#expects_call_stack_jump?" do
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one I was least satisfied with - you've fairly consistently avoided thin unit testing in favor of behavioral/usage-flavored tests. But the tests I rigged up for that here were enormous, and I decided that they were complex enough to get in the way (I needed to make custom ruby-class block-accepting matchers that expected and didn't expect call-stack jumps). But I'll take another swing if I'm not imagining that preference, and you think it's worth the ugly.

subject(:expects_call_stack_jump?) { matcher.expects_call_stack_jump? }
let(:matcher) { described_class.new(matcher_1, matcher_2) }
let(:matcher_1) { double("Matcher 1") }
let(:matcher_2) { double("Matcher 2") }

context "when neither matcher expects a call-stack jump" do
let(:matcher_1) { double("Matcher 1", :expects_call_stack_jump? => false) }
let(:matcher_2) { double("Matcher 2", :expects_call_stack_jump? => false) }
it { is_expected.to be_falsey }
end

context "when one of the matchers expects a call-stack jump" do
let(:matcher_1) { double("Matcher 1", :expects_call_stack_jump? => false) }
let(:matcher_2) { double("Matcher 2", :expects_call_stack_jump? => true) }
it { is_expected.to be_truthy }
end

context "when neither matcher _specifies_ that it expects a call-stack jump" do
let(:matcher_1) { Object.new }
let(:matcher_2) { Object.new }
it { is_expected.to be_falsey }
end
end
end
end
Loading
Loading