setup and teardown for a ruby TestSuite

My Watir tests for a particular application are grouped into three subclasses of Test::Unit::TestCase. To run them all I have a top-level test suite that looks like this:

require 'test/unit'

server.start

require 'test/first_tester'
require 'test/second_tester'
require 'test/third_tester'

server.stop

But this doesn’t work as intended, because the server.stop line at the end of the script is executed before the test suite is constructed and executed, which is obviously not what I want. The problem is in test/unit: the require causes this script to collect every test method in the ObjectSpace and then invoke the runner on the resulting suite.

What I would like is to have setup and teardown methods on TestSuite; but they aren’t there. I feel sure someone somewhere must have done this already (the closest I could find was this old post on ruby-talk), but I couldn’t find one quickly so I wrote my own:

require 'test/unit/testsuite'
require 'test/unit/ui/console/testrunner'

require 'test/first_tester'
require 'test/second_tester'
require 'test/third_tester'

class TS_MyTestSuite < Test::Unit::TestSuite
  def self.suite
    result = self.new(self.class.name)
    result << FirstTester.suite
    result << SecondTester.suite
    result << ThirdTester.suite
    return result
  end

  def setup
    server.start
  end

  def teardown
    server.stop
  end

  def run(*args)
    setup
    super
    teardown
  end
end

Test::Unit::UI::Console::TestRunner.run(TS_MyTestSuite)

Please let me know if this solution – or anything equivalent – is already published elsewhere, because I hate re-inventing wheels…

Advertisements

10 thoughts on “setup and teardown for a ruby TestSuite

  1. Try this:

    at_exit {p ‘server.stop’}
    require ‘test/unit’
    p ‘server.start’
    require ‘a_test’

    I’ve used this several times. Because of the way that test/unit runs test cases (in an at_exit) and how at_exits get unrolled this works

    -andy

  2. Nice job.

    With this you can avoid fixtures, and place database population in your setup and database clear out in your teardown.

    This saves me a headache, as I’ve got a complex schema with all sorts of proper data modelling in it, which makes fixtures impossible.

    I also didn’t like the overhead of setup and teardown of data for EVERY unit test in a test file, its not DRY at all going down that route.

  3. Hal fulton has a book titled “The Ruby Way” and he has a snippet of code to solve this problem. The method described above may work if you are running rails tests, but some people (like myself) are testing things completely unrelated to rails. Basically it works like this:
    In your Test

    class << self
    def startup
    puts ‘this will be run only once and before any test runs’
    end
    def shutdown
    puts ‘this will be run at the end of the tests’
    end
    def suite
    mysuite = super
    def mysuite.run(*args)
    RegistrationTest.startup()
    super
    RegistrationTest.shutdown()
    end
    mysuite
    end
    end

    The example given in the book is slightly different. I used the methods startup and shutdown instead of the ones he gave because in the next version of ruby test::unit they will have this support built in (and then you can remove the suite method override that we have).

  4. Oops, I left the name of my class in the previous post so it probably didnt make sense.

    Let me give you a more concrete example so its not confusing:

    class MyTest < Test::Unit::TestCase

    class << self
    def startup
    puts ‘run before any tests have started’
    end
    def shutdown
    puts ‘run after all tests have finished’
    end
    def suite
    mysuite = super
    def mysuite.run(*args)
    MyTest.startup()
    super
    MyTest.shutdown()
    end
    mysuite
    end
    end

    def setup
    puts ‘run before each test’
    end

    def teardown
    puts ‘run after each test’
    end
    end

  5. I am using the Aptana Studio for writing ruby.
    I see the following methods in : Test::Unit::TestCase base class –

    # Called before every test method runs. Can be used
    # to set up fixture information.
    def setup

    end

    # Called after every test method runs. Can be used to tear
    # down fixture information.
    def teardown

    end

    Filling up these two methods worked like a charm!.

  6. Did you ever come to any other conclusions as to finding the best way to execute a non-stop test suite in Ruby? I’m using Ruby (Test::Unit) and Selenium to execute automated tests but the browser session closes after each test which is not desirable. I’d like to find a way to have the setup method called only once at the beginning, then have each test run back to back followed by the teardown method.
    I’m not totally clear as to how I can use the example you gave in a selenese kind of way. Any advice would be greatly appreciated!

    • @Chris, I gave up on Test::Unit several years ago, and now I use Cucumber and RSpec. So I haven’t revisited this problem, because I solve my needs in a totally different way.

      I’m curious though — why do you find it undesirable to close the browser between tests?

      • Well, it may be due to the fact that I’m new to the field and haven’t found a more optimal way to run (and write) my tests. Since I’m mainly dealing with automated testing right now, I’ve broken long processes down into smaller tests. When trying to run each test as part of a suite, I run into a problem when the browser session closes because the user gets logged out. So, either I could add steps to login the user again at the beginning of each test, or I could just run one very long test, which I don’t think is best practice (but as I said, I’m pretty green so maybe that would be best!). I really like Selenium due to its ease of use and it’s worth mentioning that I can indeed run individual tests as part of a suite in the IDE without having this problem, but I need something a bit more robust.
        I’ve been enjoying your writings by the way…thanks!

  7. @Chris, I do two things to alleviate that problem. First, my end-to-end tests each login separately at the start of the test; it adds a few microseconds to the test, but it gives me a clean slate for the scenario and allows more flexibility. Second. I write as much code as possible in domain objects that are independent of the user/session context; these objects (the “middle hexagon”) can be unit tested independently, and give a lot of confidence about whether the end-to-end tests will work. See https://silkandspinach.net/2005/03/22/the-middle-hexagon-should-be-independent-of-the-adapters/ and http://www.growing-object-oriented-software.com/ for a lot more on this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s