From 658524ffa501e1b581abe9bdcc8d4c83047ad4c3 Mon Sep 17 00:00:00 2001 From: Matthias Zirnstein Date: Wed, 17 Mar 2021 21:40:31 +0100 Subject: [PATCH 1/3] Proposal: Inform user when testing a private method as a migration path to safely move from send to public_send --- Gemfile | 2 ++ features/its.feature | 2 +- lib/rspec/its.rb | 31 ++++++++++++++++++++++++++++++- spec/rspec/its_spec.rb | 13 +++++++++++++ spec/spec_helper.rb | 2 ++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index b734079..aa5f11e 100644 --- a/Gemfile +++ b/Gemfile @@ -29,3 +29,5 @@ gem 'contracts', '< 0.16' if RUBY_VERSION < '1.9.0' gem 'coveralls', :require => false, :platform => :mri_20 eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') + +gem 'pry' diff --git a/features/its.feature b/features/its.feature index c53092b..64c0c8f 100644 --- a/features/its.feature +++ b/features/its.feature @@ -61,7 +61,7 @@ Feature: attribute of subject Person with one phone number (555-1212) phone_numbers.first - is expected to eq "555-1212" + should eq "555-1212" """ Scenario: specify value of an attribute of a hash diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index 4eb83ba..c14f728 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -95,7 +95,7 @@ module Its # # # This ... # describe Array do - # its(:size, :focus) { should eq(0) } + # its(:size, focus: true) { should eq(0) } # end # # # ... generates the same runtime structure as this: @@ -131,7 +131,31 @@ def its(attribute, *options, &block) else attribute_chain = attribute.to_s.split('.') attribute_chain.inject(subject) do |inner_subject, attr| + + # When NilClass: user didnt set the rspec setting + # When FalseClass: skip this block + if RSpec.configuration.its_private_method_debug.is_a? NilClass + + # respond_to? only looks in the public method space + # TODO Check NoMethodError + # TODO Check if inner_subject some kind of const + # inner_subject.method_defined?(attr) && !inner_subject.respond_to?(attr) + if !inner_subject.respond_to?(attr) + + if RSpec.configuration.its_private_method_debug + # TODO if debug: show spec:line_num for better indication + puts its_caller.first + end + + puts "You are testing a private method. Set RSpec its_private_method_debug setting to show more info or hide it completly." + + # TODO Add deprecation warning to drop private method calling + # for the next major release + end + end + inner_subject.send(attr) + end end end @@ -175,6 +199,11 @@ def should_not(matcher=nil, message=nil) end RSpec.configure do |rspec| + + # Add RSpec configuration setting to allow + # the user to handle private method invoking warning + rspec.add_setting :its_private_method_debug + rspec.extend RSpec::Its rspec.backtrace_exclusion_patterns << %r(/lib/rspec/its) end diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index ebc59d5..22649e4 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -9,6 +9,19 @@ module RSpec its([]) { expect(described_class).to be Its } end end + context "" do + subject do + Class.new do + private + + def name + 'Maria' + end + end.new + end + + its(:name, :focus) { should eq('Maria') } + end context "with explicit subject" do subject do Class.new do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3433ee9..0face89 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,4 +12,6 @@ def method_missing(method, *args, &block) RSpec.configure do |config| config.run_all_when_everything_filtered = true config.order = 'random' + + # config.its_private_method_debug = true end From ff86eb6933d1431a0b772b1a63f990ae9a40d766 Mon Sep 17 00:00:00 2001 From: Matthias Zirnstein Date: Fri, 19 Mar 2021 22:14:26 +0100 Subject: [PATCH 2/3] Add deprecation warning and RSpec option to explicitly raise when calling a private method --- Gemfile | 2 -- features/its.feature | 4 ++-- lib/rspec/its.rb | 28 ++++++++++++---------------- spec/rspec/its_spec.rb | 25 +++++++++++++++++++++---- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Gemfile b/Gemfile index aa5f11e..b734079 100644 --- a/Gemfile +++ b/Gemfile @@ -29,5 +29,3 @@ gem 'contracts', '< 0.16' if RUBY_VERSION < '1.9.0' gem 'coveralls', :require => false, :platform => :mri_20 eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') - -gem 'pry' diff --git a/features/its.feature b/features/its.feature index 64c0c8f..58f4855 100644 --- a/features/its.feature +++ b/features/its.feature @@ -51,7 +51,7 @@ Feature: attribute of subject person end - its("phone_numbers.first") { should eq("555-1212") } + its("phone_numbers.first") { is_expected.to eq("555-1212") } end end """ @@ -61,7 +61,7 @@ Feature: attribute of subject Person with one phone number (555-1212) phone_numbers.first - should eq "555-1212" + is expected to eq "555-1212" """ Scenario: specify value of an attribute of a hash diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index c14f728..0f7b8ee 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -1,6 +1,14 @@ require 'rspec/its/version' require 'rspec/core' +RSpec.deprecate < Date: Wed, 24 Mar 2021 21:16:03 +0100 Subject: [PATCH 3/3] Use Rspec.deprecate only to indicate private method testing --- lib/rspec/its.rb | 25 +++++++++---------------- spec/rspec/its_spec.rb | 24 ++++++++++++++++-------- spec/spec_helper.rb | 2 ++ 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index 0f7b8ee..1685966 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -1,13 +1,13 @@ require 'rspec/its/version' require 'rspec/core' -RSpec.deprecate < its_caller.first) end inner_subject.send(attr) diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index cd579b1..df9eca7 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -1,5 +1,12 @@ require 'spec_helper' +# class RSpec::Core::Formatters::DeprecationFormatter +# Formatters.register self, :baem + +# def baem(notification) +# output << 'baem' +# end +# end module RSpec describe Its do describe "#its" do @@ -11,31 +18,32 @@ module RSpec end context "raises error when calling a private method" do - around(:each) do |example| - RSpec.configuration.its_raise_errors_for_private_method_calling = true - example.run - RSpec.configuration.its_raise_errors_for_private_method_calling = false - end - subject do Class.new do private def name + 'Maria' end def self.name + 'Maria' end private_class_method :name end end + before(:each) do + # expect(RSpec).to receive(:deprecate) + # .with('Testing private method name is deprecated', anything) + end + context 'on an instance' do - its('new.name') { will raise_error } + its('new.name') { should eq('Maria') } end context 'on a class' do - its('name') { will raise_error } + its('name') { should eq('Maria') } end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0face89..a7c9128 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,5 +13,7 @@ def method_missing(method, *args, &block) config.run_all_when_everything_filtered = true config.order = 'random' + # config.raise_errors_for_deprecations! + # config.its_private_method_debug = true end