llaskin
llaskin

Reputation: 809

Having minitest run tests in sequence instead of parallel?

Given the following code:

require 'minitest/autorun'
require '<some other stuff>

class test <  MiniTest::Unit::TestCase
  def setup
<setup some stuff>
  end
  def teardown
     <teardown some stuff>
  end

  def test1
    <test1 code>
  end
  def test2
    <test2 code>
  end
end

How can I make it so that minitest runs both test1 and test2 using the initial setup? The reason I am asking, is because setup instantiates a Selenium Webdriver instance and does some time consuming login/setup, and I'd like to use the same instance(instead of instantiating a new instance every time) to shorten the time it takes to test the feature.

Upvotes: 0

Views: 2075

Answers (4)

blowmage
blowmage

Reputation: 8984

There are a lot of assumptions being made in this question that I will try to address. First, Minitest doesn't run the tests in parallel. By default Minitest runs tests in random order. You can change the order the tests are run by overriding the test_order method on the test class.

class MyTest <  MiniTest::Unit::TestCase
  def self.test_order
    :sorted # Or :alpha, they both behave the same
  end
end

But, that won't get you to what I think you are asking. This will only make it so that test1 is always run before test2. This is only useful if you have written your tests such they need to run in order. (Alphabetical order, not necessarily the order they are defined in the class.)

Instance variables within a test class are not shared between tests. Meaning, test1 is run on a different instance of MyTest than test2 is run on. In order to share objects between tests you need to set them in global or class variables.

class MyTest <  MiniTest::Unit::TestCase
  def setup
    @@this_thing ||= begin
      # Really expensive operation here
    end
  end
end

Hope this helps.

Upvotes: 3

knut
knut

Reputation: 27885

Do you need Minitest, or can you change to test-unit?

With test-unit you may use 'Test::Unit::TestCase.startupandTest::Unit::TestCase.shutdown':

gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
#~ require '<some other stuff>

class MyTest <  Test::Unit::TestCase
  def self.startup
    puts '<setup some stuff>'
  end
  def self.shutdown
     puts '<teardown some stuff>'
  end

  def test1
    puts '<test1 code>'
  end
  def test2
    puts '<test2 code>'
  end
end

See also can't get test unit startup to work in ruby 1.9.2

Upvotes: 0

Justin Ko
Justin Ko

Reputation: 46846

You can do what you want by creating a custom test runner type that has a before and after all tests method. You could then create a selenium-webdriver instance before all tests and close it after all tests.

Here is an example of starting a browser and going to Google before all tests. Each test then re-uses the same browser.

require 'minitest/autorun'
require 'selenium-webdriver'

#Add before and after suite hooks using a custom runner
class MyMiniTest
  class Unit < MiniTest::Unit
    def _run_suite(suite, type)
      begin
        suite.before_suite if suite.respond_to?(:before_suite)
        super(suite, type)
      ensure
        suite.after_suite if suite.respond_to?(:after_suite)
      end
    end
  end
end
MiniTest::Unit.runner = MyMiniTest::Unit.new

class GoogleTest < MiniTest::Unit::TestCase
  def self.before_suite
    p "before all tests"
    @@driver = Selenium::WebDriver.for :firefox
    @@driver.navigate.to 'http://www.google.com'
  end

  def self.after_suite
    p "after all tests"
    @@driver.quit
  end

  def setup
    p 'setup before each test'
  end

  def teardown
    p 'teardown after each test'
  end

  def test1
    p 'test1'
    assert_equal(0, @@driver.find_elements(:xpath, '//div').length)
  end

  def test2
    p 'test2'
    assert_equal(0, @@driver.find_elements(:xpath, '//a').length)
  end
end

You can see the order that things are run by the output:

"before all tests"
"setup before each test"
"test1"
"teardown after each test"
"setup before each test"
"test2"
"teardown after each test"
"after all tests"

Note that variables you want to share across tests will need to be class variables (ie prefixed with @@).

Upvotes: 0

Tony Hopkinson
Tony Hopkinson

Reputation: 20330

It's bad idea from a testing point of view, but

You could do both tests in one test and reuse the setup

Or take it out of setup and teardown and add a helper method to set it up if it isn't already and call that from the test. First test that runs takes the hit, other's just reuse it.

What you should be doing however is a mock or stub in the unit test, and leaving the real deal to an integration test.

Upvotes: 0

Related Questions