Unable to access functions in modules via scope operator - ruby

I am working on a program in Ruby with Watir that has a series of modules.
An example module would look like:
demoMod1.rb
DEMOMOD1 = true #this module has been loaded
module demoMod1
def reload(browser)
browser.goto("demo1.html")
end
end
There are several files with very similarly named functions (example: reload() ), so demoMod2 and demoMod3 both have the same reload function (except pointing to different pages).
The modules are commented out of the require list when that area is not expected to be tested. (as per design specs)
The program then dynamically loads test case files (code to do that is below in main.rb). Each test case file contains a runTest() function, which calls the appropriate sections from the modules. Each test checks if the module is loaded, and if it has, the test executes.
A sample test case file would look like:
test1.rb
def runTest(browser) # test case 1
if(defined?(DEMOMOD1)).nil?
return nil #if the module is not there, dont run this test
end
demoMod1::reload(browser)
end
As per specs, a single file lists all of the modules to be required, so that a user can just comment out the sections to not be tested without having to look at the main file. The main file is below.
main.rb
require 'Watir'
SITE = #internal site - address removed =/ sorry
FOLDER = File.dirname(__FILE__) + '/lib'
SHORTNAME = 'Test' #for reporting
LONGNAME = 'In Development' #for reporting
#I know - globals.. yuck... avoid...
require FOLDER + '/lib/requires'
#includes the function to require each module from the module folder
require FOLDER + '/www/include/report.module'
#creates an html report to view the test results
# Get list of tests
def getTests()
Dir.chdir(FOLDER + '/Tests')
files = Dir.glob("*.rb")
return files
end #eofunc
# Run each test
def runTests(files, browser, report)
testSuite = String.new
files.each do |test|
suite = test.split(" - ") # name convention: suite - testname.rb
if !(suite[0]==testSuite)
testSuite = suite[0]
report.newTestSuiteName(testSuite)
end #eoif
load FOLDER + '/Tests/' + test #load the test case file
runTest(browser) #run the test case
end #eoeach
end #eofunc
begin
browser = Watir::Browser.new
browser.goto(SITE)
report = Report.new()
reportname = report.createReport(SHORTNAME, LONGNAME) #vars in Vars.rb
files = getTests()
runTests(files, browser, report)
report.finishReport(reportname, SITE)
browser.close
report.openReport(reportname) #launch report in a new browser
end #eofunc
I would expect in each of the runTest() to be able to access the demoMod1::reload() and demoMod2::reload(), but this is not the case. I get an error for undefined method 'reload' for demoMod1:Module. I can resolve the error by using include demoMod1 but then (if I have this correctly), the scope is not needed, and using just reload() would work. However this would require each module have entirely unique names, and that is not a great solution.
What am I missing?

Have you tried using a Test/Unit / rSpec or something similar to run your tests?
Beyond that, my understanding is:
Requiring the file the module is in only makes it available for inclusion. You need to use include to put it into a class in order to use it.
Have you considered making a class for your tests, e.g. Test and then TestType1 and TestType2 inheriting from that parent class?
For example:
require fileWithYourModules
class Test
# stuff I want to have available to all tests
end
class TestSubType1 < Test
include demoMod1
end
class TestSubType2 < Test
include demoMod2
end
Which then allows you to call the same method name and have it behave differently depending on the test class it has.
I'm sure there are more elegant solutions, I'm just a tester and not a programmer! :)

By changing the function names in the modules I can get it to work.
example module:
DEMOMOD1 = true
module DemoMod1
def DemoMod1.reload(browser) #<---------
browser.goto('demo1.htm')
end
end
Line with the <--- arrow is the main change (also had to change the module name, did not have a capital first letter...oops)
example test case:
def runTest(browser) # test case 1
if(defined?(DEMOMOD1)).nil?
return nil #if the module is not there, dont run this test
end
DemoMod1::reload(browser)
### OR ### Both work
DemoMod1.reload(browser)
end

Related

ruby catch classes as they are defined

Building tile games or simulations in Ruby Gosu always makes me end upp with a list of all available tiles, saved by their class. For example [Pipe, PipeJunktion, Box, Pump] and so on. Each class is defined in one of a few separate files, which i required from the main program. For now i have to add the class myself to this list every time I add a new tile to the game. I was wondering if there was a way to catch all loading classes from a file.
Something along the lines of:
allTiles = []
require_relative 'tiles.rb'.each_class {|class| allTiles << class}
would be handy.
Or can this be solved with modules in some way?
Checking which classes were added by a file is not something that's easily or commonly done. A better approach would be to put all the tile classes under a single namespace. Since classes can be re-opened, these can be split among multiple files.
class Tiles
class Pipe
# ...
end
end
class Tiles
class Box
# ...
end
end
Then Tiles.constants could would return an array of symbols: [:Pipe, :Box], and could be used to get a list of class references using Tiles.constants.map { |const| Tiles.const_get const } or Tiles.constants.map &Tiles.method(:const_get)
If for whatever reason it was really important to know which constants were added by a specific file, the following code shows an approach:
constants1 = Object.constants
require "./tiles.rb"
constants2 = Object.constants
added_constants = constants2 - constants1
If tiles.rb had class definitions for Pipe and Box, then added_constants would be [:Pipe, :Box].
The problem with this approach is that might show constants added by gems, for example:
constants1 = Object.constants
require 'mechanize'
class Foo
end
constants2 = Object.constants
added_constants = constants2 - constants1
Since I called require 'mechanize', the added_constants list will be quite long and include much more than just Foo.
You can do something like this:
Dir['tiles/*.rb'].each { |file| require file }
What would collect all files from a tiles subfolder and requires it.
In a next step load all classes by their file names:
all_tiles = Dir['tiles/*.rb'].map do |file|
file_name = File.basename(x, '.*')
camel_cased_name = file_name.split('_').collect(&:capitalize).join
Object.const_get(camel_cased_name)
end
Btw the same can be done in Rails like this:
all_tiles = Dir['tiles/*.rb'].map do |file|
File.basename(x, '.*').camelize.constantize
end
I suspect there are pitfalls with the following approach, but I will put it out and invite comments.
First, use ObjectSpace::each_object to compile a list of all classes that exist before any custom classes have been created:
base_classes = ObjectSpace.each_object(Class).to_a
For my version of Ruby (2.4.0), within IRB, base_classes.size #=> 490. Now load the code with require's etc. Suppose that causes three classes to be created:
class A; end
class B; end
class C; end
Now compile a list of all classes that now exist and subtract base_classes:
ObjectSpace.each_object(Class).to_a - base_classes
#=> [A, B, C]
This returns an array of classes that have been added by my code.
Of course this does not show classes in base_classes that are overridden by my code or show which classes are defined by required gems.

Spec Testing a Ruby CLI

I am trying to test the first ruby CLI i've written (n00b alert) and need some help. All my code is within 1 file, this includes a Class, OptionParser and some basic class execution methods. Here's an idea of what that looks like
The *rb. file
require 'optparse'
require 'fileutils'
class Foo
attr_accessor :arg, :opt
def initialize(p={})
#opt = p[:opt] || false
end
def do_something(arg)
#arg = arg
end
#more methods...
end
# Options
#options={}
#opt_parser = OptionParser.new do |opt|
opt.banner = "<{ FooBar }>"
opt.separator "------------"
opt.on("-o", "--opt", "An Option" do
#options[:opt] = true
end
end
#opt_parser.parse!
#CLI Execution
#foo = Foo.new(#options)
#foo.do_something(ARGV[0])
So here is the problem, i know would like to run some rspec tests rspec spec/ that i've wrote for the class, however the lines outside the class get executed of course and im left with an ARGV error.
What im looking for
Is there a better way to organize my code so i can test all the pieces, or how could i write a test to accommodate this file, Any and all suggestions would be greatly appreciated.
One posible solution is to wrap your option parsing code with a conditional that checks if the file is being run directly or loaded by some other file.
if __FILE__ == $0
# option parsing code
end
If you do that then all the code inside the if __FILE__ == $0 will not run with your test, but the rest of the code will run normally.

How can I get rid of already initialized constant warnings when requiring files inside env.rb?

I require some files in features/support/env.rb as:
['/helpers/*', '/pages/*', '/models/*'].each do |path|
Dir[File.dirname(__FILE__) + path].each { |file| require file }
end
(I do it as I'd want to create test users and some other stuff before any of my tests are run.)
But it seems those files are then loaded by Cucumber using load as I get a ton of warnings like when Cucumber loads them:
/home/andrey/dev/project/features/support/models/my_class.rb:2: warning: already initialized constant MyClass::MY_CONSTANT
when scenarios start. How can I get rid of those warnings?
You can wrap your code in a silence_warnings block:
silence_warnings do
['/helpers/*', '/pages/*', '/models/*'].each do |path|
Dir[File.dirname(__FILE__) + path].each { |file| require file }
end
end
There's probably a better way to to whatever it is that you're trying to do, in a way that will play nice with your test framework, but the code above should handle your immediate question.
You probably can setup your helpers and models in a cucumber Before hook.
The recommended way to run a before hook only once is to use a global variable, so:
Before do
if !$already_required
['/helpers/*', '/pages/*', '/models/*'].each do |path|
Dir[File.dirname(__FILE__) + path].each { |file| require file }
end
$already_required = true
end
end
(https://github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

Before/After Suite when using Ruby MiniTest

Is there an alternative to RSpec's before(:suite) and after(:suite) in MiniTest?
I suspect that a custom test runner is in order, however I cannot imagine it is not a common requirement, so somebody has probably implemented in. :-)
There are setup() and teardown() methods available. The documentation also lists before() and after() as being available.
Edit: Are you looking to run something before each test or before or after the whole suite is finished?
As noted above in Caley's answer and comments, MiniTest::Unit contains the function after_tests. There is no before_tests or equivalent, but any code in your minitest_helper.rb file should be run before the test suite, so that will do the office of such a function.
Caveat: Still relatively new at Ruby, and very new at Minitest, so if I'm wrong, please correct me! :-)
To get this to work with the current version of Minitest (5.0.6) you need to require 'minitest' and use Minitest.after_run { ... }.
warn "MiniTest::Unit.after_tests is now Minitest.after_run. ..."
https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb
https://github.com/seattlerb/minitest/blob/master/lib/minitest/unit.rb
To run code before each test, use before. You're operating here in the context of an instance, possibly of a class generated implicitly by describe, so instance variables set in before are accessible in each test (e.g. inside an it block).
To run code before all tests, simply wrap the tests in a class, a subclass of MiniTest::Spec or whatever; now, before the tests themselves, you can create a class or module, set class variables, call a class method, etc., and all of that will be available in all tests.
Example:
require "minitest/autorun"
class MySpec < MiniTest::Spec
class MyClass
end
def self.prepare
puts "once"
##prepared = "prepared"
##count = 0
end
prepare
before do
puts "before each test"
#local_count = (##count += 1)
end
describe "whatever" do
it "first" do
p MyClass
p ##prepared
p #local_count
end
it "second" do
p MyClass
p ##prepared
p #local_count
end
end
end
Here's the output, along with my comments in braces explaining what each line of the output proves:
once [this code, a class method, runs once before all tests]
Run options: --seed 29618 [now the tests are about to run]
# Running tests:
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
1 [the instance variable from the before block is visible in each test]
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
2 [the instance variable from the before block is visible each test]
(Note that I do not mean this output to imply any guarantee about the order in which tests will run.)
Another approach is to use the existing before but wrap code to run only once in a class variable flag. Example:
class MySpec < MiniTest::Spec
##flag = nil
before do
unless ##flag
# do stuff here that is to be done only once
##flag = true
end
# do stuff here that is to be done every time
end
# ... tests go here
end
One simple way to do this is to write a guarded class method, and then call that in a begin.
A Minitest::Spec example:
describe "my stuff" do
def self.run_setup_code
if #before_flag.nil?
puts "Running the setup code"
#before_flag = true
end
end
before do
self.class.run_setup_code
end
it "will only run the setup code once" do
assert_equal 1, 1
end
it "really only ran it once" do
assert_equal 1,1
end
end
...to get
Run options: --seed 11380
# Running:
Running the setup code
..
Finished in 0.001334s, 1499.2504 runs/s, 1499.2504 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
You can just place the code outside of the class.
This is what I do to have a banner.
require 'selenium-webdriver'
require 'minitest/test'
require 'minitest/autorun'
class InstanceTest < Minitest::Test
def setup
url = ARGV.first
#url = self.validate_instance(url)
#driver = Selenium::WebDriver.for :firefox
end
Nice thing about minitest is its flexibility. I've been using a custom MiniTest Runner with a +before_suite+ callback. Something like in this example - Ruby Minitest: Suite- or Class- level setup?
And then tell minitest to use the custom runner
MiniTest::Unit.runner = MiniTestSuite::Unit.new
You can also add an after test callback by updating your test_helper.rb (or spec_helper.rb) like this
# test_helper.rb
class MyTest < Minitest::Unit
after_tests do
# ... after test code
end
end

ruby: how to load .rb file in the local context

How this simple task can be done in Ruby?
I have some simple config file
=== config.rb
config = { 'var' => 'val' }
I want to load config file from some method, defined in main.rb file so that the local variables from config.rb became local vars of that method.
Something like this:
=== main.rb
Class App
def loader
load('config.rb') # or smth like that
p config['var'] # => "val"
end
end
I know that i can use global vars in config.rb and then undefine them when done, but i hope there's a ruby way )
The config file.
{ 'var' => 'val' }
Loading the config file
class App
def loader
config = eval(File.open(File.expand_path('~/config.rb')).read)
p config['var']
end
end
As others said, for configuration it's better to use YAML or JSON. To eval a file
binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb")
binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")
This syntax would allow you to see filename in backtraces which is important. See api docs [1].
Updated eval command to avoid FD (file descriptor) leaks. I must have been sleeping or maybe should have been sleeping at that time of the night instead of writing on stackoverflow..
[1] http://www.ruby-doc.org/core-1.9.3/Binding.html
You certainly could hack out a solution using eval and File.read, but the fact this is hard should give you a signal that this is not a ruby-like way to solve the problem you have. Two alternative designs would be using yaml for your config api, or defining a simple dsl.
The YAML case is the easiest, you'd simply have something like this in main.rb:
Class App
def loader
config = YAML.load('config.yml')
p config['var'] # => "val"
end
end
and your config file would look like:
---
var: val
I do NOT recommend doing this except in a controlled environment.
Save a module to a file with a predetermined name that defines an initialize and run_it methods. For this example I used test.rb as the filename:
module Test
##classvar = 'Hello'
def initialize
#who = 'me'
end
def get_who
#who
end
def run_it
print "#{##classvar} #{get_who()}"
end
end
Then write a simple app to load and execute it:
require 'test'
class Foo
include Test
end
END {
Foo.new.run_it
}
# >> Hello me
Just because you can do something doesn't mean you should. I cannot think of a reason I'd do it in production and only show it here as a curiosity and proof-of-concept. Making this available to unknown people would be a good way to get your machine hacked because the code could do anything the owning account could do.
I just had to do a similar thing as I wanted to be able to load a "Ruby DLL" where it returns an anonymous class ( a factory for instances of things ) I created this which keeps track of items already loaded and allows the loaded file to return a value which can be anything - a totally anonymous Class, Module, data etc. It could be a module which you could then "include" in an object after it is loaded and it could could supply a host of "attributes" or methods. you could also add an "unload" item to clear it from the loaded hash and dereference any object it loaded.
module LoadableModule
##loadedByFile_ = {};
def self.load(fileName)
fileName = File.expand_path(fileName);
mod = ##loadedByFile_[fileName];
return mod if mod;
begin
Thread.current[:loadReturn] = nil;
Kernel.load(fileName);
mod = Thread.current[:loadReturn];
##loadedByFile_[fileName] = mod if(mod);
rescue => e
puts(e);
puts(e.backtrace);
mod = nil;
end
Thread.current[:loadReturn] = nil;
mod
end
def self.onLoaded(retVal)
Thread.current[:loadReturn] = retVal;
end
end
inside the loaded file:
LoadableModule.onLoaded("a value to return from the loaded file");

Resources