best practice to tear down classes defined inside describe blocks

22 views
Skip to first unread message

Jean-Michel Garnier

unread,
Oct 14, 2016, 4:34:54 PM10/14/16
to rspec
Hi,

Not sure this is the best place, traffic seems quite low these days.

I am wondering what is the best practice to tear down classes declared inside `describe` blocks.
For example:

```ruby # ./spec/foo_spec.rb require 'spec_helper' describe 'Foo' do class Person < ActiveRecord::Base has_many :abilities end it 'has relations' do expect(Person.reflections.keys).to eq %w(abilities) end end # ./spec/bar_spec.rb require 'spec_helper' describe 'Bar' do class Person < ActiveRecord::Base has_many :children end it 'has relations' do expect(Person.reflections.keys).to eq %w(children) end end ```

Running each individual file passes but when I run both files then it fails (in both files with `["children", "abilities"]` in relations array).

To solve that problem, we use an `after(:all)` block to remove the constant with `Object.send(:remove_const, 'Person').

I'd love to hear if other people use that tear down or what is the best practice.

Thanks,

JM

Myron Marston

unread,
Oct 14, 2016, 4:41:43 PM10/14/16
to rs...@googlegroups.com

My suggestion is to use anonymous classes declared in-line:

# ./spec/foo_spec.rb
require 'spec_helper'

describe 'Foo' do

  let(:person_class) do
    Class.new(ActiveRecord::Base) do
      has_many :abilities
    end

  end

  it 'has relations' do

    expect(person_class.reflections.keys).to eq %w(abilities)

  end
end

# ./spec/bar_spec.rb
require 'spec_helper'

describe 'Bar' do

  let(:person_class) do
    Class.new(ActiveRecord::Base) do
      has_many :children
    end

  end

  it 'has relations' do

    expect(person_class.reflections.keys).to eq %w(children)
  end
end

There are two nice things about this:

  • No cleanup is needed. Since the classes are anonymous, there are no constant references to them and once the example completes Ruby’s garbage collector will take care of it.
  • There’s no possibility of re-opening an existing class. Class.new always makes a new class.

The downside is that the class does not have a constant, which might not play nice with activerecord (it may not support anonymous subclasses) and it will be less pretty in error messages.

If you want to make it a named class, you can use RSpec’s constant stubbing; it exists specifically for this:

# ./spec/foo_spec.rb
require 'spec_helper'

describe 'Foo' do

  before do
    stub_const("Person", Class.new(ActiveRecord::Base) do
      has_many :abilities
    end)
  
end

  it 'has relations' do
    expect(Person.reflections.keys).to eq %w(abilities)
  end
end

# ./spec/bar_spec.rb
require 'spec_helper'

describe 'Bar' do

  before do
    stub_const("Person", Class.new(ActiveRecord::Base) do
      has_many :children
    end)
  
end

  it 'has relations' do
    expect(Person.reflections.keys).to eq %w(children)
  end
end

RSpec will take care of cleaning up the Person constant created in each example, ensuring the constants do not leak and cause problems.

HTH,
Myron

--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+unsubscribe@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/8e469a6b-1175-4299-bf99-c90ffd587ca6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Reply all
Reply to author
Forward
0 new messages