Alternative to def @instance_variable ||= ... usable in before(:all)

9 views
Skip to first unread message

Maurizio De Santis

unread,
Apr 14, 2014, 9:27:18 AM4/14/14
to rs...@googlegroups.com
Hello everyone,

firstly I'm sorry for my poor english, I hope I'll manage to be clear; don't hesitate to ask for clarifications if I will not.

In my tests this is a common pattern:

describe Hello do
  def a
    @a ||= 'a'
  end

  def b
    @b ||= 'b'
  end

  def c
    @c ||= 'c'
  end

  let(:d) { 'd' }
  let(:e) { 'e' }
  let(:f) { 'f' }

  before(:all) do
    a
    b
    c
  end
end

I have to use def ... for the methods used inside before(:all), due to the problems described in https://github.com/rspec/rspec-core/issues/500 .

My problem with it is that I don't like its readability: it isn't DRY and it doesn't express what I intend.

My ideal pattern would be like this:

describe Hello do
  let(:a) { 'a' }
  let(:b) { 'b' }
  let(:c) { 'c' }
  let(:d) { 'd' }
  let(:e) { 'e' }
  let(:f) { 'f' }

  before(:all) do
    a
    b
    c
  end
end

But I know it is utopian, since the problem described in the issue above.

So I would like to have your opinions about something like (notice set instead of let):

describe Hello do
  set(:a) { 'a' }
  set(:b) { 'b' }
  set(:c) { 'c' }
  let(:d) { 'd' }
  let(:e) { 'e' }
  let(:f) { 'f' }

  before(:all) do
    a
    b
    c
  end
end

Or (maybe even better) something like:

describe Hello do
  let(:a, :all) { 'a' }
  let(:b, :all) { 'b' }
  let(:c, :all) { 'c' }
  let(:d) { 'd' }
  let(:e) { 'e' }
  let(:f) { 'f' }

  before(:all) do
    a
    b
    c
  end
end

I think it is better with "let, argument indicating using in before(:all)" because it is unnecessary to create another method, and it is more "explicit" about what it's doing.

At the moment, I'm using the following (as poor as effective) implementation:

module RSpecSet
  def set(name, &block)
    instance_variable_name = :"@#{name}"

    define_method(name) do
      return instance_variable_get instance_variable_name if instance_variable_defined? instance_variable_name
     
      instance_variable_set instance_variable_name, block.call
    end
  end

  def set!(name, &block)
    define_method(name) { instance_variable_set :"@#{name}", block.call }
  end
end

Rspec.config { |c| c.extend RSpecSet }

Where set memoizes the result into an instance variable, while set! does not.

Looking at rspec-core-3.0.0.beta2/lib/rspec/core/memoized_helpers.rb , where let and let! are implemented, I guess that an implementation more similar to it would be more appropriate, but I haven't figured out it yet.

What do you think about it? Am I the only with this need? Are you happy with the "if not in before_all let else def" approach? Do you have ideas in order to improve my RSpecSet?

Thank you all and long live to RSpec :)

Myron Marston

unread,
Apr 14, 2014, 11:09:35 AM4/14/14
to rs...@googlegroups.com
You can also do:

describe Hello do
  attr_accessor :a, :b, :c

  before(:all) do
    self.a = 'a'
    self.b = 'b'
    self.c = 'c'
  end
end
  
Or, if you don't mutate the values, you can just do:

describe Hello do
  a = 'a'
  b = 'b'
  c = 'c'
end

...since local variables can be accessed from within any hooks or examples since blocks are closures.

HTH,
Myron
Reply all
Reply to author
Forward
0 new messages