There is a class in a ruby file test.rb
#test.rb
class AAA<TestCase
def setUp()
puts "setup"
end
def run()
puts "run"
end
def tearDown()
end
end
In another file test2.rb, i want to get the instance of AAA by file name "test.rb".
In python, i can do this by below:
casename = __import__ ("test")
for testcase in [getattr(casename, x) for x in dir(casename)]:
if type(testcase) is type and issubclass(testcase, TestCase):
#do something with testcase()
How can i implement the same function in ruby now.
Thanks
Just require the filename without the .rb extension like so:
require './test'
Suppose you have this directory structure:
+-- project/
| |
| +-- folder1/
| | |
| | +-- file1.rb
| |
| +-- folder2/
| |
| +-- file2.rb
|
+-- file3.rb
in this case you may want to add specific directories to the load path like so:
# in file3.rb
$LOAD_PATH.unshift './folder1'
this way you can require files by their name without specifying the folder every time:
require 'file1'
Now for the second part, getting an instance. You could just do AAA.new but i suppose you want to dynamically create instances of classes that are subclasses of TestCase. First you have to find all subclasses of TestCase:
class Class
def subclasses
constants.map do |c|
const_get(c)
end.select do |c|
c.is_a? Class
end
end
end
this will enable you to get a list of subclasses like so:
TestCase.subclasses
#=> [TestCase::AAA]
from which you can construct your objects
TestCase.subclasses.map{|klass| klass.new }
#=> [#<TestCase::AAA:0x007fc8296b07f8>]
or even shorter if you do not need to pass arguments to new
TestCase.subclasses.map(&:new)
#=> [#<TestCase::AAA:0x007fc8296b07d0>]
So far, so good. But if i get this right you are trying to build a test runner. Don't. There are plenty of testing libraries and great test runners out there. Ruby has the built-in Minitest and this blog post explains very well how you can best run your tests.
Related
I am using Rspec in my project where I would like to print the time taken by each testcase, Is there any way Rspec is providing any prebuilt function? I can take the starting time of the testcase by example.execution_result.started_at but I don't know how to take the end time of testcase, If I can take the end time, then I can subtract the end time from starting time to get the time duration for each testcase. Is there any one help me at this place? I have written this code
around(:each) do |example|
startTime=Time.now
var=example.run
puts var
endTime=Time.now
duration=endTime-startTime
puts "Time Taken->#{duration.to_f/60.to_f}"
end
But I strongly believe Rspec must be giving some predefined method to return the duration of each testcase, do you anyone know that?
RSpec has a example_status_persistence_file_path configuration that generates a file with the run time for each individual test.
For example, given the following RSpec configuration/examples:
require 'rspec/autorun'
# Enable the reporting
RSpec.configure do |c|
c.example_status_persistence_file_path = 'some_file.txt'
end
# Run some tests
RSpec.describe 'some thing' do
it 'does stuff' do
sleep(3)
end
it 'does more stuff' do
sleep(2)
end
end
A report of each example's status and run time is generated:
example_id | status | run_time |
--------------- | ------ | ------------ |
my_spec.rb[1:1] | passed | 3.02 seconds |
my_spec.rb[1:2] | passed | 2.01 seconds |
If you want more detail and/or want to control the formatting, you can create a custom formatter.
For example, given the following specs:
RSpec.describe 'some thing' do
it 'does stuff' do
sleep(3)
raise('some error')
end
it 'does more stuff' do
sleep(2)
end
end
Output - Text
We can add a custom formatter to output the full test description, status, run time and exception:
class ExampleFormatter < RSpec::Core::Formatters::JsonFormatter
RSpec::Core::Formatters.register self
def close(_notification)
#output_hash[:examples].map do |ex|
output.puts [ex[:full_description], ex[:status], ex[:run_time], ex[:exception]].join(' | ')
end
end
end
RSpec.configure do |c|
c.formatter = ExampleFormatter
end
This gives us:
some thing does stuff | failed | 3.010178 | {:class=>"RuntimeError", :message=>"some error", :backtrace=>["my_spec.rb:21:in `block... (truncated for example)
some thing does more stuff | passed | 2.019578 |
The output can be modified to add headers, have nicer formatting, etc.
Output - CSV
The formatter can be modified to output to a CSV:
require 'csv'
class ExampleFormatter < RSpec::Core::Formatters::JsonFormatter
RSpec::Core::Formatters.register self
def close(_notification)
with_headers = {write_headers: true, headers: ['Example', 'Status', 'Run Time', 'Exception']}
CSV.open(output.path, 'w', with_headers) do |csv|
#output_hash[:examples].map do |ex|
csv << [ex[:full_description], ex[:status], ex[:run_time], ex[:exception]]
end
end
end
end
RSpec.configure do |c|
c.add_formatter(ExampleFormatter, 'my_spec_log.csv')
end
Which gives:
Example,Status,Run Time,Exception
some thing does stuff,failed,3.020176,"{:class=>""RuntimeError"", :message=>""some error"", :backtrace=>[""my_spec.rb:25:in `block...(truncated example)"
some thing does more stuff,passed,2.020113,
Every example gets an ExecutionResult object which has a run_time method, so example.execution_result.run_time should give you what you’re asking for
I would like to modify the way some html tags are rendered on jekyll. What I need is to add some css classes automatically (in this case the ".table" class to the table html tag).
I'm using the redcarpet markdown processor. I suppose I need to write a plugin that extends the renderer but I can't find any good example...
I came up with this but it's just a copy/paste job and it doesn't work...
require 'redcarpet'
class BootstrapTables < Redcarpet::Render::HTML
def table(header, body)
"\n<table class='table'><thead>\n#{ header }</thead><tbody>\n#{ body }</tbody></table>\n"
end
end
Someone can help?
I've tested that you can give a class to a markdown element with kramdown
{:.foo}
| First Header | Second Header |
| ------------- | ------------- |
| Content Cell | Content Cell |
| Content Cell | Content Cell |
Your table has the class foo
NB : this answer was given one month ago on SO
I found the way and corrected the code, I need a properly configured custom renderer. Using RedcarpetExt as markdown variable in the _config.yaml will activate it.
# Create a custom renderer that extend Redcarpet to customize its behavior.
require 'jekyll'
require 'redcarpet'
class RedcarpetExtender < Redcarpet::Render::HTML
# Extend the table to be compatible with the Bootstrap css.
def table(header, body)
"\n<table class='table-bordered table-hover'><thead>\n#{ header }</thead><tbody>\n#{ body }</tbody></table>\n"
end
end
class Jekyll::Converters::Markdown::RedcarpetExt
def initialize(config)
#site_config = config
end
def extensions
Hash[ *#site_config['redcarpet']['extensions'].map {|e| [e.to_sym, true]}.flatten ]
end
def markdown
#markdown ||= Redcarpet::Markdown.new(RedcarpetExtender, extensions)
end
def convert(content)
return super unless #site_config['markdown'] == 'RedcarpetExt'
markdown.render(content)
end
end
Another solution can be to use sass Bootstrap version.
This version is in synch with twbs/bootstrap and sass/scss is natively supported by Jekyll.
Then once you have sass working (is takes five minutes) you just have to create a rule in your custom style.scss file :
table{
#extend .table;
}
And then every table will use .table bootstrap rules.
Is it possible to get the location of the file which requires another file in Ruby?
I have a project where I spawn some processes and I would love to be able, in the code, to determine which file is the parent of the required file. This is nice when debugging.
Example:
#initial.rb:
require "./my_file.rb"
fork do
require "./my_file2.rb"
end
-
#my_file.rb:
puts "Required from file: #{?????}"
-
#my_file2.rb:
require "./my_file.rb"
I would expect to get something like:
#=> Required from file: /path/to/initial.rb
#=> Required from file: /path/to/my_file2.rb
Based on Jacobs answer I ended with this redefinition of require_relative and require:
alias :old_require_relative :require_relative
def require_relative(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require_relative arg
end
alias :old_require :require
def require(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require arg
end
In a test test scenario with the following load sequence:
test.rb
+- test1.rb
+- test1_a.rb
+ test2.rb
The following calls
require './test1'
require './test2'
or
require_relative 'test1'
require_relative 'test2'
result in:
test.rb requires ./test1
C:/Temp/test1.rb requires test1_a
test.rb requires ./test2
You could also include the line of the requirement in the output.
You should never need to do this, but you can examine the call stack from Kernel#caller. You'll have to filter out require methods (especially if you use any libraries that override require).
I'm writing some ruby (not Rails) and using test/unit with shoulda to write tests.
Are there any gems that'll allow me to implement traceability from my tests back to designs/requirements?
i.e.: I want to tag my tests with the name of the requirements they test, and then generate reports of requirements that aren't tested or have failing tests, etc.
Hopefully that's not too enterprisey for ruby.
Thanks!
Update: This solution is available as a gem: http://rubygems.org/gems/test4requirements
Are there any gems that'll allow me to implement traceability from my
tests back to designs/requirements?
I don't know any gem, but your need was inspiration for a little experiment, how it could be solved.
You have to define your Requirements with RequirementList.new(1,2,3,4)
This requirements can be assigned with requirements in a TestCase
each test can be assigned to a requirement with requirement
after the test results you get an overview which requirements are tested (successfull)
And now the example:
gem 'test-unit'
require 'test/unit'
###########
# This should be a gem
###########
class Test::Unit::TestCase
def self.requirements(req)
##requirements = req
end
def requirement(req)
raise RuntimeError, "No requirements defined for #{self}" unless defined? ##requirements
caller.first =~ /:\d+:in `(.*)'/
##requirements.add_test(req, "#{self.class}##{$1}")
end
alias :run_test_old :run_test
def run_test
run_test_old
#this code is left if a problem occured.
#in other words: if we reach this place, then the test was sucesfull
if defined? ##requirements
##requirements.test_successfull("#{self.class}##{#method_name}")
end
end
end
class RequirementList
def initialize( *reqs )
#requirements = reqs
#tests = {}
#success = {}
#Yes, we need two at_exit.
#tests are done also at_exit. With double at_exit, we are after that.
#Maybe better to be added later.
at_exit {
at_exit do
self.overview
end
}
end
def add_test(key, loc)
#fixme check duplicates
#tests[key] = loc
end
def test_successfull(loc)
#fixme check duplicates
#success[loc] = true
end
def overview()
puts "Requirements overiew"
#requirements.each{|req|
if #tests[req] #test defined
if #success[#tests[req]]
puts "Requirement #{req} was tested in #{#tests[req] }"
else
puts "Requirement #{req} was unsuccessfull tested in #{#tests[req] }"
end
else
puts "Requirement #{req} was not tested"
end
}
end
end #RequirementList
###############
## Here the gem end. The test will come.
###############
$req = RequirementList.new(1,2,3, 4)
class MyTest < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
requirements $req
def test_1()
requirement(1) #this test is testing requirement 1
assert_equal(2,1+1)
end
def test_2()
requirement(2)
assert_equal(3,1+1)
end
def test_3()
#no assignment to requirement 3
pend 'pend'
end
end
class MyTest_4 < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
requirements $req
def test_4()
requirement(4) #this test is testing requirement 4
assert_equal(2,1+1)
end
end
the result:
Loaded suite testing_traceability_solutions
Started
.FP.
1) Failure:
test_2(MyTest)
[testing_traceability_solutions.rb:89:in `test_2'
testing_traceability_solutions.rb:24:in `run_test']:
<3> expected but was
<2>.
2) Pending: pend
test_3(MyTest)
testing_traceability_solutions.rb:92:in `test_3'
testing_traceability_solutions.rb:24:in `run_test'
Finished in 0.65625 seconds.
4 tests, 3 assertions, 1 failures, 0 errors, 1 pendings, 0 omissions, 0 notifications
50% passed
Requirements overview:
Requirement 1 was tested in MyTest#test_1
Requirement 2 was unsuccessfull tested in MyTest#test_2
Requirement 3 was not tested
Requirement 4 was tested in MyTest_4#test_4
If you think, this could be a solution for you, please give me a feedback. Then I will try to build a gem out of it.
Code example for usage with shoulda
#~ require 'test4requirements' ###does not exist/use code above
require 'shoulda'
#use another interface ##not implemented###
#~ $req = Requirement.new_from_file('requirments.txt')
class MyTest_shoulda < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
#~ requirements $req
context 'req. of customer X' do
#Add requirement as parameter of should
# does not work yet
should 'fullfill request 1', requirement: 1 do
assert_equal(2,1+1)
end
#add requirement via requirement command
#works already
should 'fullfill request 1' do
requirement(1) #this test is testing requirement 1
assert_equal(2,1+1)
end
end #context
end #MyTest_shoulda
With cucumber you can have your requirement be the test, doesn't get any more traceable than that :)
So a single requirement is a feature, and a feature has scenario that you want to test.
# addition.feature
Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
| 2 | 5 | add | 7 |
| 0 | 40 | add | 40 |
Then you have step definitions written in ruby mapped to the scenario
# step_definitons/calculator_steps.rb
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
require 'cucumber/formatter/unicode'
$:.unshift(File.dirname(__FILE__) + '/../../lib')
require 'calculator'
Before do
#calc = Calculator.new
end
After do
end
Given /I have entered (\d+) into the calculator/ do |n|
#calc.push n.to_i
end
When /I press (\w+)/ do |op|
#result = #calc.send op
end
Then /the result should be (.*) on the screen/ do |result|
#result.should == result.to_f
end
I would like to have my Sinatra app include a view specific stylesheet in the layout.
Consider this simple app:
app_folder_root/
| my_app/
| my_app.rb
| public/
| css/
| index.css
| layout.css
| views/
| index.haml
| layout.haml
config.ru
config.ru:
require 'rubygems'
require './my_app/my_app'
map '/' do
run MyApp
end
app.rb:
require 'sinatra/base'
class MyApp < Sinatra::Base
get '/' do
haml :index
end
end
I tried setting a variable in my_app.rb that sets the name of the view and tried to reference it in layout.haml, but that did not work (I probably wouldn't have went with this as a final solution to the problem since I felt this was a code smell, but was just trying different possibilities).
This is using Haml, but I am hoping that is irrelevant - thinking it should be the same for erb, etc.
In layout.haml, I would like to reference the view that will be rendered and include a view specific stylesheet by a naming convention. For example, if index.haml is going to render, I would like to include css/index.css. What is the best way to go about doing this?
I solved this by doing the following:
In index.haml (at the top) I created or appended an array named views:
- #views = Array.new unless defined? #views
- #views << 'index'
In layout.haml I reference #views:
%head
- #views.each do |view|
- haml_tag :link, {:rel => 'stylesheet', :type => 'text/css', :href => "css/#{view}.css"}
I am a little disappointed with having to check #views in the view to make sure it is defined before appending to it, but for now it is manageable.
EDIT: Solved the problem with having to check if #views is defined in each view. In config.ru add the following:
before do
#views = Array.new
end
I can now remove this line from the views:
- #views = Array.new unless defined? #views