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.

Two test methods with the same name leads to the first method not being run. You could put a flunk in the first test, and tests would still pass. One side-effect of cut and paste programming.
–
Andrew GrimmFeb 18 '09 at 1:00

Yes, and it is easy. This is finally implemented in TestUnit. See my post waaaay down this page.
–
jpgeekSep 25 '12 at 14:59

Unfortunately the second answer doesn't work with the current version of Test::Unit, at least not running in RubyMine under Windows 7. Cutting and pasting the code into RubyMine and running it, startup and shutdown each run twice, not once :( I'm only using it to log the start and end of a test case (using Logger), so I can see which test case produced which bit of the log, so I can reluctantly live with that, but other people might not be able to.
–
digitigJun 27 '13 at 1:18

What if I want to do a similar thing in ActionController::TestCase ?
–
Aakash UpadhyayJul 24 at 5:43

This answer is like 6 years old so I wouldn't be surprised if things have changed slightly since given that it relies on some pretty specific implementation details of TestUnit::TestCase.
–
Matt WolfeJul 27 at 3:55

+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

In Python, nose does what you want. So does GoogleTest in C++. I agree that sometimes you want to set-up at the class, module, or even program level. As GoogleTest docs say:

Google Test creates a new test fixture object for each test in order to make tests independent and easier to debug. However, sometimes tests use resources that are expensive to set up, making the one-copy-per-test model prohibitively expensive.

At least Ruby easily supports parameterized tests. (I'm looking for an example, and I cannot post hyperlinks yet.) However, both Ruby's test/unit and Python's unittest frameworks are sticklers for test isolation. Hopefully, the Ruby community will give ground on this point someday. (Apparently, it did, in the lastest [version 2] of test/unit.) Until then, try RSpec (which has its own set of problems).

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

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

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.

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: