How do I reset a class definition in Ruby? - 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:
Pry complains about all the methods on Foo and Foo_tests being redefined
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.

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.

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?

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.

Related

if __FILE__ == $0 break program when it's placed before any methods

I'm new to Ruby and am trying to understand a behavior that I've run into. I'm writing a class that needs some constants initialized before it can run, but when its run from another class, as it sometimes will be, I get warnings about constants already being defined. So I placed the following at the end of my file:
if __FILE__ == $0
constant_initialiation
ReviewScraper.new.getReviews($testing, $getWeekendReviews, $clearWorksheet, $getAll)
end
The constant_initialization is just a bunch of constants being set, nothing interesting. Anyway, this works great for me -- so long as its at the end of the file. If I move this up to the top and try running, I get an error: unitialized constant ReviewScraper (NameError). Almost as if it compiles sequentially for just this part of the file and isn't finding the ReviewScraper class definition when its run.
Can any Ruby geniuses explain this behavior to me? It's no big issue other than for styling purposes (I like having my list of constants up top), but it would be nice to understand what's going on here.
First off, I'll expand on my comments above as to what is going on. Secondly, I'll suggest perhaps a better way of setting your constants in code that avoids setting them multiple times.
As I mentioned, a Ruby script reads from top to bottom. So, if you try to instantiate a class before you define it, they the Ruby script won't know what it is.
cat = Cat.new # NameError
class Cat
# Code
end
cat = Cat.new # Works fine
The script first reads the line where you make a new Cat object. However, it doesn't know what a Cat is yet. Once it is done processing the code for what a Cat is, then it can create one. I used the example of talking to someone about Darth Vader but it is probably more akin to asking a construction company to build your building before you've ever handed them a blueprint. It is only after they have the blueprint that they can build a your building.
Now, in regards to initializing constants, there are a couple different things you could do. One is, you could put the initializations in a if block much like you did at the top but leave out the instantiating of the class until the end of the script. (Two if statements.) Another would be to put the constants in a module in its own file.
module Names
Dog = "Spot"
Cat = "Sparkles"
end
Now you just require that file wherever you need it. In file_one.rb you put
require_relative './modules/names_module.rb' # Or wherever it is
include Names
You put the same thing in your Review Scraper file. Here's the cool part: if you require the Names module once, it'll be brought into the code. However, if you require it a second time nothing will happen. You won't get warnings. It'll just quietly not require it a second time. One top of that, all your constants are in their own namespace.
Just a thought.

Guard Ruby classes with class-level metaprogramming against multiple loads

A quirk of Ruby's require is that, while in general, it will only load a file once, if that file is accessible via multiple paths (e.g. symlinks), it can be required multiple times. This causes problems when there are things like class-level metaprogramming, or in general a code that should only be executed once on file loading, getting executed multiple times.
Is there any way, from inside a Ruby class definition, to tell whether the class has been defined before? I thought defined? or Object.const_get might tell me, but from those it looks like the class is defined as soon as it's opened.
This is not an answer to your question in the second paragraph, but a solution to the issue in your first paragraph. Actually, you cannot avoid multiple file loads by checking whether a class was defined already.
Instead of doing:
require some_file_name
do:
require File.realpath(some_file_name)
By doing so, different symbolic links pointing to the same real file would be normalized to the same real file name, and hence multiple loading of them would be correctly filtered by require.
Cf. this question and the answer given there.
The real solution is that requiring that a piece of code is only executed once is bad design and you should fix that.
However, what you could do is simply set some flag that the code has already been executed and check that flag. E.g.:
class Foo
unless #__executed__
def bla; end
puts 'Test'
end
#__executed__ = true
end

Ruby require - execute multiple times

A general question about require in Ruby.
My understanding of Ruby's require is that the file specified is only loaded once but can be executed multiple times. Is this correct?
I have a set of Rspec tests in different files which all require the same file logger.rb. It doesn't seem like the methods I call in the required file are executed in every spec.
Here is some of the code I wrote in logger.rb, it first cleans up the temp dir an then creates a logger.
tmpdir = Dir.tmpdir
diagnostics_directory = File.join(tmpdir, LibertyBuildpack::Diagnostics::DIAGNOSTICS_DIRECTORY)
FileUtils.rm_rf diagnostics_directory
raise 'Failed to create logger' if LibertyBuildpack::Diagnostics::LoggerFactory.create_logger(tmpdir).nil?
I'd like this to happen in every spec.
Is that because the tests are within the same module or did I misunderstand how require works.
There is still a great many ifs, as you do not show the code requiring your file, but I think I have understood some of the misunderstandings you had :-)
Your statement 'The file specified is only loaded once but can be executed multiple times.' is basically the opposite of what is true. If a file is to have any effect on a ruby program it will have to be 'executed', it may just happen sometimes that one of the executed methods defines other methods or classes. All statements in a file will be executed once when loaded, but you may load a file multiple times. If you require a file it will only be loaded if it has not been done so already, so as with method definitions your 'static' method calls will only be executed once.
If you want to execute things multiple times you should either load the file (which is usually inefficient since all the compiling will have to be done again) or you require a file with a method definition in it (def ... end) and you call the method multiple times (with possibly varying parameters). The latter is the usual way, as compiling will only have to take place once this way.

RSpec - How do nested before :all and before :each blocks interact?

I encountered an issue with some Web UI Automation using RSpec and Selenium/Capybara/SitePrism. It's related to the combination of before :all and before :each clauses that occur in the code, and perhaps especially in my spec_helper file.
Up until now, I've been running rspec against one spec file at a time. Each spec file required a spec_helper file, which included the following:
spec_helper.rb:
RSpec.configure do |config|
config.before(:all) do
# 1) Code to configure WebDriver and launch Browser is here
end
end
The spec files themselves contain their own before blocks for various reasons. Most of them contain something like the following:
test_a_spec.rb:
describe "Page A" do
before :all do
# 2) Log in to web site, maybe load the test page in question
end
it "does this thing" do
# 3) Test this thing
end
it "does that thing" do
# 4) Test that thing
end
end
This worked fine as long as I was running RSpec against individual spec files. When I tried tagging some of my examples and then running against the whole spec folder, I had a problem. The before :all block in spec_helper.rb didn't prepend itself to every file, as I thought it would, but instead ran once at the beginning. All the spec files after the first were expecting a clean browser to be launched by spec_helper and to do the log in part themselves, but the browser wasn't clean and was already logged in, so that wasn't good.
Changing the before :all in spec_helper.rb to a before :each seemed like the natural solution, so I did, and suddently my tests instantly failed with an error claiming that rack-test requires a rack application, but none was given. This happened to some of my tests but not all, and through process of elimination I realized it was only tests that had their own before :all blocks that were failing. It appears that the before :all in the spec file is superceding the before :each in the spec_helper file. So it was trying to log in before it had launched a browser.
This vexes me. I am terribly vexed. I had sort of assumed two things:
I assumed before :each was on equal footing with before :all, and in fact imagine it like a before :all plus more, in a sense. The idea that a before :all would supercede a before :each seems weird to me.
I assume all these before blocks were subject to nesting in a reasonable way. That is, a before :all block should fire once before all the things below it, which would mean firing within each iteration of a before :each block that might contain the before :all block. Also, if a before :each contains some code and then a describe statement below it has a before :all, the before :each code should still fire before the before :all code. Maybe it does do that, I'm just not sure at this point.
Questions:
1) What kind of behavior is my spec_helper file actually producing? If I were to take that same behavior and put it into one of the spec files itself, for instance, what would that look like? Would the before :each with the browser launch code wrap around all the code in the spec file in some sort of implicit describe block? Does the before :each get inserted into the outermost describe block, and therefore have to compete with the spec file's before :all block?
2) I could "solve" this by abandoning all before :all blocks in my tests, but I like the flexibility of the way things are, and since this is UI Automation and speed is a factor, I don't really want to be opening a new browser and logging in for each describe, even though I'm aware that this would be ideal for keeping every test separated from the others. Do I have to/want to do just that?
Thanks!
To address the part of your question not already covered by https://stackoverflow.com/a/22489263/1008891, the require method will not reload any file already loaded previously, as described in https://stackoverflow.com/a/22489263/1008891. This contrasts with the load method, which acts like a "copy/paste" of the file contents wherever it is used.
I'm not saying that if you changed all your requires to loads that you'd get the same result as running the individual files, as there may be other global state that affects the behavior you're seeing.
The RSpec documentation (http://rspec.info/documentation/3.3/rspec-core/RSpec/Core/Hooks.html#before-instance_method) provides some very useful information about how before blocks interact and the order in which they are run, but they do recommend avoiding before(:context) [aka before(:all)].
FYI, note that the after blocks are run in the reverse order fin which before blocks are.

Passing a parameter/object to a ruby unit/test before running it using TestRunner

I'm building a tool that automates a process then runs some tests on it's own results then goes to do some other stuff.
In trying to clean up my code I have created a separate file that just has the test cases class. Now before I can run these tests, I have to pass the class a couple of parameters/objects before they can be run. Now the problem is that I can't seem to find a way to pass a parameter/object to the test class.
Right now I am thinking to generate a Yaml file and read it in the test class but it feels "wrong" to use a temporary file for this. If anyone has a nicer solution that would be great!
**************Edit************
Example Code of what I am doing right now:
#!/usr/bin/ruby
require 'test/unit/ui/console/testrunner'
require 'yaml'
require 'TS_SampleTestSuite'
automatingSomething()
importantInfo = getImportantInfo()
File.open('filename.yml', 'w') do |f|
f.puts importantInfo.to_yaml
end
Test::Unit::UI::Console::TestRunner.run(TS_SampleTestSuite)
Now in the example above TS_SampleTestSuite needs importantInfo, so the first "test case" is a method that just reads in the information from the Yaml file filname.yml.
I hope that clears up some confusion.
Overall, it looks like you're not really using the unit tests in a very rubyish way, but I'll leave that aside for a minute.
Your basic problem is that you have some setup that needs to happen before the tests run. The normal way to do that is with a setup method within the test unit case itself.
class UserTest < TestUnit::TestCase
def setup
# do your important calculation
end
def test_success
#.. assert some things
end
end
I would give some thought to what code it is that you're actually testing here, and see if you can break it down and test it in a more granular way, with lots more tests.
First, I agree with Cameron, this code definitely does not adhere to the Ruby way, though I'll also sidestep that for now.
The fastest way to get up and running with this, especially if this data is pretty much immutable (that is to say, your tests won't be altering it in anyway), is to just assign the value to a constant. So instead of naming your variable importantInfo, you name it IMPORTANT_INFO. Then it will be available to you in your tests. It's definitely not a pretty solution, and I think it couuld even be considered a test smell that you need that sort of global setup, but it's there for you.
Alternatively, you could look at stubbing out the importantInfo, which I actually think would provide for much cleaner and more readable tests.

Resources