Please consider the below code
class Execute
def self.inherited(klass)
puts "Class Inherited: #{klass}"
klass.run
end
def self.run
puts "Running Instance Methods"
instance_methods(false).each do |m|
if m.to_s.start_with?("test_")
puts "Executing Method: #{m}"
new.send(m)
end
end
end
end
puts "Before class definition"
class Raja < Execute
def test_1
puts 'test_1'
end
def test_2
puts 'test_2'
end
def test_3
puts 'test_3'
end
end
puts "After class definition"
I am currently making an attempt to access the test_1, test_2, and test_3 methods by instantiating an object from the parent class. Upon examination of the program, one may comprehend the actions I am performing. Nonetheless, the issue arises as the invocation of the instance_methods method returns an empty array, contrary to its expected behavior of providing the names of the instance methods, i.e., test_1, test_2, and test_3. I am at a loss as to why this discrepancy is occurring and would greatly appreciate any assistance in resolving this issue
Class#inherited is called at the moment of inheritance.
At this point no instance_methods (non-inherited instance methods since you used false) have been defined.
class Raja < Execute #inherited is called here
# methods are defined here
end
It would be similar to the following
class Raja;end
Raja.instance_methods(false) #=> []
class Raja
def test_1 = 'test1'
end
You could possibly look into Module#method_added instead however I am unclear on what the reasoning behind this code is so all I can do is answer the question of Why you are experiencing what you you are experiencing.
Update
Based on OP comments looking for an auto run mechanism similar to test-unit:
As a very simplified Example:
class Tracker
##runner = {}
def self.runner
##runner
end
def self.inherited(klass)
runner[klass] = []
end
def self.method_added(method_name)
runner[self] << method_name if method_name.match?(/test_/)
end
end
class Runner
def self.run
Tracker.runner.each do |klass,tests|
tests.each do |m|
klass.new.send(m)
end
end
end
end
# Run the Runner before the program exits
at_exit { Runner.run}
Then (foo.rb)
require 'runner_tracker'
class Foo < Tracker
def test_1
puts 'test_1'
end
def test_2
puts 'test_2'
end
def test_3
puts 'test_3'
end
end
Output
> ruby foo.rb
test_1
test_2
test_3
I am supposed to make this test pass.
describe 'new' do
it "takes a parameter and returns a HangpersonGame object" do
#hangpersonGame = HangpersonGame.new('glorp')
expect(#hangpersonGame).to be_an_instance_of(HangpersonGame)
expect(#hangpersonGame.word).to eq('glorp')
expect(#hangpersonGame.guesses).to eq('')
expect(#hangpersonGame.wrong_guesses).to eq('')
end
end
My code is this,
class HangpersonGame
attr_accessor :word
attr_accessor :guesses
attr_accessor :wrong_guesses
def initialize(word)
#word = word
#guesses = nil
#wrong_guesses = nil
end
end
The test is failing. Why? I have run the code seperately and it seems to work.
I'm playing around with Ruby's Test::Unit::TestCase, and though my tests will run, pass, fail, etc. I'm not seeing the dots outputted for each test-case. Is this a configuration I need to set, or a level of verbosity I need to specify?
I'm running Ruby 2.1.0p0
For reference, here's the code I'm working with. It's from a Destroy All Software screencast where the exercise is to build rspec from scratch (not the whole thing, of course):
The tests:
#test_spec.rb
require 'test/unit'
require_relative 'spec'
class TestDescribe < Test::Unit::TestCase
def test_that_it_can_pass
describe 'some thing' do
it 'has some property' do
end
end
end
def test_that_it_can_fail
assert_raise(IndexError) do
describe 'some failing thing' do
it 'fails' do
raise IndexError
end
end
end
end
end
class TestAssertion < Test::Unit::TestCase
def test_that_it_can_pass
2.should == 2
end
def test_that_it_can_fail
assert_raise(AssertionError) do
1.should == 2
end
end
end
And the code:
#spec.rb
def describe(description, &block)
ExampleGroup.new(block).evaluate!
end
class ExampleGroup
def initialize(block)
#block = block
end
def evaluate!
instance_eval(&#block)
end
def it(description, &block)
block.call
end
end
class Object
def should
DelayedAssertion.new(self)
end
end
class DelayedAssertion
def initialize(subject)
#subject = subject
end
def ==(other)
raise AssertionError unless #subject == other
end
end
class AssertionError < Exception
end
The output when run with ruby test_spec.rb
Run options:
# Running tests:
Finished tests in 0.004410s, 907.0295 tests/s, 453.5147 assertions/s.
4 tests, 2 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]
I think a TestCase represents a single test. It's not responsible for running, or providing a UI. I think you're looking for a "runner". In minitest, you can simply require 'minitest/autorun', and in Test::Unit (which is deprecated), you'd probably use Test::Unit::Runner somehow. I use rspec so I don't know the exact details, but both Test::Unit and minitest are included in ruby stdlib so the documentation should be easy to find.
I upgraded 'test-unit' and I got the dots back. At the moment of writing this the version is 3.1.8.
require 'rubygems'
require 'test/unit'
class Thing
attr_accessor :foo
def set_stuff
#foo = 'bar'
end
end
class ThingTest < Test::Unit::TestCase
def setup
#thing = Thing.new
end
def test_set_stuff
#thing.set_stuff
assert 'bar' == #thing.foo
end
def test_foo_in_other_test
puts #thing.foo
assert 'bar' == #thing.foo
end
end
# Loaded suite testing
# Started
# nil
# F.
# Finished in 0.00439 seconds.
#
# 1) Failure:
# test_foo_in_other_test(ThingTest) [testing.rb:26]:
# <false> is not true.
#
# 2 tests, 2 assertions, 1 failures, 0 errors
The difference looks to be that you're not calling #thing.set_stuff in the second test.
I'm not as familiar with Test::Unit as RSpec, but I believe the setup() method will be called each time a test is run. So one #thing will be overwritten by another.
In addition, I've found that you can't assume a particular order for the execution of test cases; often (perhaps all the time?) the tests are run from last to first, as seems to be the case in this instance.
I'm struggling with Test::Unit. When I think of unit tests, I think of one simple test per file. But in Ruby's framework, I must instead write:
class MyTest < Test::Unit::TestCase
def setup
end
def test_1
end
def test_1
end
end
But setup and teardown run for every invocation of a test_* method. This is exactly what I don't want. Rather, I want a setup method that runs just once for the whole class. But I can't seem to write my own initialize() without breaking TestCase's initialize.
Is that possible? Or am I making this hopelessly complicated?
As mentioned in Hal Fulton's book "The Ruby Way".
He overrides the self.suite method of Test::Unit which allows the test cases in a class to run as a suite.
def self.suite
mysuite = super
def mysuite.run(*args)
MyTest.startup()
super
MyTest.shutdown()
end
mysuite
end
Here is an example:
class MyTest < Test::Unit::TestCase
class << self
def startup
puts 'runs only once at start'
end
def shutdown
puts 'runs only once at end'
end
def suite
mysuite = super
def mysuite.run(*args)
MyTest.startup()
super
MyTest.shutdown()
end
mysuite
end
end
def setup
puts 'runs before each test'
end
def teardown
puts 'runs after each test'
end
def test_stuff
assert(true)
end
end
FINALLY, test-unit has this implemented! Woot!
If you are using v 2.5.2 or later, you can just use this:
Test::Unit.at_start do
# initialization stuff here
end
This will run once when you start your tests off. There are also callbacks which run at the beginning of each test case (startup), in addition to the ones that run before every test (setup).
http://test-unit.rubyforge.org/test-unit/en/Test/Unit.html#at_start-class_method
That's how it's supposed to work!
Each test should be completely isolated from the rest, so the setup and tear_down methods are executed once for every test-case. There are cases, however, when you might want more control over the execution flow. Then you can group the test-cases in suites.
In your case you could write something like the following:
require 'test/unit'
require 'test/unit/ui/console/testrunner'
class TestDecorator < Test::Unit::TestSuite
def initialize(test_case_class)
super
self << test_case_class.suite
end
def run(result, &progress_block)
setup_suite
begin
super(result, &progress_block)
ensure
tear_down_suite
end
end
end
class MyTestCase < Test::Unit::TestCase
def test_1
puts "test_1"
assert_equal(1, 1)
end
def test_2
puts "test_2"
assert_equal(2, 2)
end
end
class MySuite < TestDecorator
def setup_suite
puts "setup_suite"
end
def tear_down_suite
puts "tear_down_suite"
end
end
Test::Unit::UI::Console::TestRunner.run(MySuite.new(MyTestCase))
The TestDecorator defines a special suite which provides a setup and tear_down method which run only once before and after the running of the set of test-cases it contains.
The drawback of this is that you need to tell Test::Unit how to run the tests in the unit. In the event your unit contains many test-cases and you need a decorator for only one of them you'll need something like this:
require 'test/unit'
require 'test/unit/ui/console/testrunner'
class TestDecorator < Test::Unit::TestSuite
def initialize(test_case_class)
super
self << test_case_class.suite
end
def run(result, &progress_block)
setup_suite
begin
super(result, &progress_block)
ensure
tear_down_suite
end
end
end
class MyTestCase < Test::Unit::TestCase
def test_1
puts "test_1"
assert_equal(1, 1)
end
def test_2
puts "test_2"
assert_equal(2, 2)
end
end
class MySuite < TestDecorator
def setup_suite
puts "setup_suite"
end
def tear_down_suite
puts "tear_down_suite"
end
end
class AnotherTestCase < Test::Unit::TestCase
def test_a
puts "test_a"
assert_equal("a", "a")
end
end
class Tests
def self.suite
suite = Test::Unit::TestSuite.new
suite << MySuite.new(MyTestCase)
suite << AnotherTestCase.suite
suite
end
end
Test::Unit::UI::Console::TestRunner.run(Tests.suite)
The Test::Unit documentation documentation provides a good explanation on how suites work.
Well, I accomplished basically the same way in a really ugly and horrible fashion, but it was quicker. :) Once I realized that the tests are run alphabetically:
class MyTests < Test::Unit::TestCase
def test_AASetup # I have a few tests that start with "A", but I doubt any will start with "Aardvark" or "Aargh!"
#Run setup code
end
def MoreTests
end
def test_ZTeardown
#Run teardown code
end
It aint pretty, but it works :)
To solve this problem I used the setup construct, with only one test method followed. This one testmethod is calling all other tests.
For instance
class TC_001 << Test::Unit::TestCase
def setup
# do stuff once
end
def testSuite
falseArguments()
arguments()
end
def falseArguments
# do stuff
end
def arguments
# do stuff
end
end
I know this is quite an old post, but I had the issue (and had already written classes using Tes/unit) and ave answered using another method, so if it can help...
If you only need the equivalent of the startup function, you can use the class variables:
class MyTest < Test::Unit::TestCase
##cmptr = nil
def setup
if ##cmptr.nil?
##cmptr = 0
puts "runs at first test only"
##var_shared_between_fcs = "value"
end
puts 'runs before each test'
end
def test_stuff
assert(true)
end
end
I came across this exact problem and created a subclass of Test::Unit::TestCase for doing exactly what you describe.
Here's what I came up with. It provides it's own setup and teardown methods that count the number of methods in the class that begin with 'test'. On the first call to setup it calls global_setup and on the last call to teardown it calls global_teardown
class ImprovedUnitTestCase < Test::Unit::TestCase
cattr_accessor :expected_test_count
def self.global_setup; end
def self.global_teardown; end
def teardown
if((self.class.expected_test_count-=1) == 0)
self.class.global_teardown
end
end
def setup
cls = self.class
if(not cls.expected_test_count)
cls.expected_test_count = (cls.instance_methods.reject{|method| method[0..3] != 'test'}).length
cls.global_setup
end
end
end
Create your test cases like this:
class TestSomething < ImprovedUnitTestCase
def self.global_setup
puts 'global_setup is only run once at the beginning'
end
def self.global_teardown
puts 'global_teardown is only run once at the end'
end
def test_1
end
def test_2
end
end
The fault in this is that you can't provide your own per-test setup and teardown methods unless you use the setup :method_name class method (only available in Rails 2.X?) and if you have a test suite or something that only runs one of the test methods, then the global_teardown won't be called because it assumes that all the test methods will be run eventually.
Use the TestSuite as #romulo-a-ceccon described for special preparations for each test suite.
However I think it should be mentioned here that Unit tests are ment to run in total isolation. Thus the execution flow is setup-test-teardown which should guarantee that each test run undisturbed by anything the other tests did.
I created a mixin called SetupOnce. Here's an example of using it.
require 'test/unit'
require 'setuponce'
class MyTest < Test::Unit::TestCase
include SetupOnce
def self.setup_once
puts "doing one-time setup"
end
def self.teardown_once
puts "doing one-time teardown"
end
end
And here is the actual code; notice it requires another module available from the first link in the footnotes.
require 'mixin_class_methods' # see footnote 1
module SetupOnce
mixin_class_methods
define_class_methods do
def setup_once; end
def teardown_once; end
def suite
mySuite = super
def mySuite.run(*args)
#name.to_class.setup_once
super(*args)
#name.to_class.teardown_once
end
return mySuite
end
end
end
# See footnote 2
class String
def to_class
split('::').inject(Kernel) {
|scope, const_name|
scope.const_get(const_name)
}
end
end
Footnotes:
http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html
http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/
+1 for the RSpec answer above by #orion-edwards. I would have commented on his answer, but I don't have enough reputation yet to comment on answers.
I use test/unit and RSpec a lot and I have to say ... the code that everyone has been posting is missing a very important feature of before(:all) which is: #instance variable support.
In RSpec, you can do:
describe 'Whatever' do
before :all do
#foo = 'foo'
end
# This will pass
it 'first' do
assert_equal 'foo', #foo
#foo = 'different'
assert_equal 'different', #foo
end
# This will pass, even though the previous test changed the
# value of #foo. This is because RSpec stores the values of
# all instance variables created by before(:all) and copies
# them into your test's scope before each test runs.
it 'second' do
assert_equal 'foo', #foo
#foo = 'different'
assert_equal 'different', #foo
end
end
The implementations of #startup and #shutdown above all focus on making sure that these methods only get called once for the entire TestCase class, but any instance variables used in these methods would be lost!
RSpec runs its before(:all) in its own instance of Object and all of the local variables are copied before each test is run.
To access any variables that are created during a global #startup method, you would need to either:
copy all of the instance variables created by #startup, like RSpec does
define your variables in #startup into a scope that you can access from your test methods, eg. ##class_variables or create class-level attr_accessors that provide access to the #instance_variables that you create inside of def self.startup
Just my $0.02!