Search code examples
rubyrspecrspec-rails

Rspec how to determine if a let block has been defined?


In Rspec, I want to take advantage of using super() to call a defined let block if it exists or set a new value if it hasn't, I want to use this within a shared_example group but I just can't find how to do this.

I've tried checking if @some_let exists, I've tried checking if a super method of :some_let is owned by the kernel or not and none of them provide anything useful; I can't access instance_methods or instance_method because Rspec won't let me and searching the internet for a method hasn't revealed an answer.

I want to be able to do something like this:

shared_examples 'a shared example' do
  let(:some_let) { let_exists?(:some_let) ? super() : some_new_value }
end

is there a method like let_exists? or something to that effect?


Solution

  • Assuming that you call let before including the shared examples, this would work:

    shared_examples 'a shared example' do
      let(:some) { 'fallback value' } unless method_defined?(:some)
    end
    
    describe 'with let' do
      let(:some) { 'explicit value' }
      include_examples 'a shared example'
    
      it { expect(some).to eq('explicit value') }
    end
    
    describe 'without let' do
      include_examples 'a shared example'
    
      it { expect(some).to eq('fallback value') }
    end
    

    method_defined? checks if a method called some has already been defined in the current context. If not, the method is defined to provide a default value.

    Another (usually easier) approach is to always define a default value and to provide the explicit value after including the shared examples: (thus overwriting the default value)

    shared_examples 'a shared example' do
      let(:some) { 'default value' }
    end
    
    describe 'with let' do
      include_examples 'a shared example'   # <- order is
      let(:some) { 'explicit value' }       #    important
    
      it { expect(some).to eq('explicit value') }
    end
    
    describe 'without let' do
      include_examples 'a shared example'
    
      it { expect(some).to eq('default value') }
    end