Blaise Pascal
Blaise Pascal

Reputation: 945

How do I reset a class definition in Ruby?

How can I completely remove a class from memory in Ruby?

I have two files I'm working with:

# foo.rb
require 'expensive_library'

class Foo < ExpensiveLibrary::Plugin
   ...
end

and:

# foo_tests.rb
require 'foo'
require 'test/unit'

class foo_tests < Test::Unit::TestCase
  def test_foo_meets_spec_1
    ...
  end
  def test_foo_meets_deprecated_spec
    ...
  end
end

ExpensiveLibrary is expensive; it takes over 15 seconds to load. This is way too long to repeatedly run the tests during development (the rest of the test suite takes less than 1 second).

I have worked around the load time of the expensive library by starting Pry and writing a function that loads the two files and calls Test::Unit:Autorunner.run. This still has a 15 second pause in the first run of the tests, but subsequent test runs take less than 1 second each.

However, there are two problems:

  1. Pry complains about all the methods on Foo and Foo_tests being redefined
  2. when I remove a method from the source file, it remains defined in my test environment, generating spurious failures.

Based on other Stack Overflow questions (like "How to undefine class in Ruby?"), I have tried calling Object.constants.remove(:Foo) and Object.constants.remove(:Foo_tests). I no longer get the method redefined errors, but now the autorunner runs the tests multiple times, including removed tests.

What I want is to run the modified tests without reloading ExpensiveLibrary. Undefining the test classes is the way I see to do it, but I don't know how. Other solutions might be better.

Upvotes: 7

Views: 526

Answers (3)

tudorpavel
tudorpavel

Reputation: 149

It seems to me that what you're looking for is a preloader such as Spork, or even a more specific one like rspec-preloader. When using a preloader, you will have to require your 'foo' file in a spec/spec_helper.rb file, which you probably already have:

# spec/spec_helper.rb
require 'foo'

# ...

The spec_helper should load your entire app environment. Arguably, it's more expensive when running tests one at a time. But by using a preloader, you only have to require all the code once.

Upvotes: 1

faron
faron

Reputation: 1059

You can hack with load paths (Adding a directory to $LOAD_PATH (Ruby)).

  • create some folder like ./tests/fake_implementations/
  • add it to $LOAD_PATHS
  • add there file expensive_library.rb with some lightweight fake implementation of ExpensiveLibrary
  • then require 'expensinsive_library' will load this file instead of the original one.

Upvotes: 0

Darren Kirby
Darren Kirby

Reputation: 86

OK, just spitballing here, because I'm not certain exactly what you're trying to, but have you tried undef or undef_method? They don't work directly on classes, but if you undef the methods that call the tests you don't want done maybe it will work for you?

Upvotes: 0

Related Questions