如何在Ruby中重置类定义?

时间:2022-10-30 08:04:22

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

如何在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).

昂贵的图书馆很贵;加载需要15秒以上。这在开发过程中反复运行测试的时间太长(测试套件的其余部分不到1秒)。

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.

我通过启动Pry并编写一个加载两个文件并调用Test :: Unit:Autorunner.run的函数来解决昂贵库的加载时间问题。在第一次测试中,这仍然有15秒的暂停,但后续测试运行每次不到1秒。

However, there are two problems:

但是,有两个问题:

  1. Pry complains about all the methods on Foo and Foo_tests being redefined
  2. Pry抱怨重新定义了Foo和Foo_tests上的所有方法

  3. when I remove a method from the source file, it remains defined in my test environment, generating spurious failures.
  4. 当我从源文件中删除一个方法时,它仍然在我的测试环境中定义,产生虚假的失败。

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.

根据其他Stack Overflow问题(如“如何在Ruby中取消定义类?”),我尝试调用Object.constants.remove(:Foo)和Object.constants.remove(:Foo_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.

我想要的是在不重新加载ExpensiveLibrary的情况下运行修改过的测试。取消定义测试类是我看到的方式,但我不知道如何。其他解决方案可能更好。

3 个解决方案

#1


1  

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:

在我看来,你正在寻找的是像Spork这样的预加载器,或者甚至是像rspec-preloader这样的更具体的预加载器。使用预加载器时,您必须在spec / spec_helper.rb文件中要求您的'foo'文件,您可能已经拥有该文件:

# 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.

spec_helper应该加载整个应用环境。可以说,一次运行一个测试会更昂贵。但是通过使用预加载器,您只需要一次所有代码。

#2


0  

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?

好吧,只是在这里吐痰,因为我不确定你到底想要什么,但你尝试过undef或undef_method吗?它们不直接在类上工作,但是如果你取消调用你不希望完成的测试的方法,它可能适合你吗?

#3


0  

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

你可以破解加载路径(将目录添加到$ LOAD_PATH(Ruby))。

  • create some folder like ./tests/fake_implementations/
  • 创建一些像./tests/fake_implementations/这样的文件夹

  • add it to $LOAD_PATHS
  • 将其添加到$ LOAD_PATHS

  • add there file expensive_library.rb with some lightweight fake implementation of ExpensiveLibrary
  • 使用ExpensiveLibrary的一些轻量级伪实现添加文件expensive_library.rb

  • then require 'expensinsive_library' will load this file instead of the original one.
  • 然后要求'expensinsive_library'将加载此文件而不是原始文件。

#1


1  

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:

在我看来,你正在寻找的是像Spork这样的预加载器,或者甚至是像rspec-preloader这样的更具体的预加载器。使用预加载器时,您必须在spec / spec_helper.rb文件中要求您的'foo'文件,您可能已经拥有该文件:

# 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.

spec_helper应该加载整个应用环境。可以说,一次运行一个测试会更昂贵。但是通过使用预加载器,您只需要一次所有代码。

#2


0  

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?

好吧,只是在这里吐痰,因为我不确定你到底想要什么,但你尝试过undef或undef_method吗?它们不直接在类上工作,但是如果你取消调用你不希望完成的测试的方法,它可能适合你吗?

#3


0  

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

你可以破解加载路径(将目录添加到$ LOAD_PATH(Ruby))。

  • create some folder like ./tests/fake_implementations/
  • 创建一些像./tests/fake_implementations/这样的文件夹

  • add it to $LOAD_PATHS
  • 将其添加到$ LOAD_PATHS

  • add there file expensive_library.rb with some lightweight fake implementation of ExpensiveLibrary
  • 使用ExpensiveLibrary的一些轻量级伪实现添加文件expensive_library.rb

  • then require 'expensinsive_library' will load this file instead of the original one.
  • 然后要求'expensinsive_library'将加载此文件而不是原始文件。