Global setup and teardown blocks in Test::Unit
Asked Answered
B

2

21

What's the best way to have a setup run before every method in an entire test suite (not just one test class)?

Rspec allows you to define global before and after blocks. Is there a clean comparable way to do this in Test::Unit that doesn't involve mixing a module into each test class?

Belay answered 15/11, 2009 at 4:16 Comment(2)
I'm also really interested in this. I'm using mongoid with cucumber and all of the examples to clear the db between tests use Rspec. I don't really like the solution below as it doesn't allow me to also have individual setup/teardown methods in each test, along with a global one.Wop
possible duplicate of In Ruby's Test::Unit::TestCase, how do I override the initialize method?Urchin
J
7

You could just patch Test::Unit::TestCase and define a setup method:

class Test::Unit::TestCase
  def setup
    puts 'in setup'
  end
end

And your subclasses would just use this by default:

class FooTest < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

class BarTest < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

If a test case needed to have its own setup, you would need to call super first to ensure that the global setup runs:

class BazTest < Test::Unit::TestCase
  def setup
    super
    puts 'custom setup'
  end

  def test_truth
    assert true
  end
end

Is having a global setup really something you need to do, or would it be helpful to have a helper method defined on Test::Unit::TestCase and call that in the tests that need it? The helper method approach is something that I find beneficial on my projects – the setup state and intention is clearer in each individual test and I don't need to jump around to find some "hidden" setup method. Quite often, a global setup is a code smell indicating that you need to rethink part of your design, but YMMV.

Update

Since you're using ActiveSupport, here's a first stab at something that won't require a call to super each time you define a setup method in your test case. I don't know how valuable it is, since it requires a call to a different method and any developer can just define their own setup method in the test case that will invalidate this change. Here it is:

require 'rubygems'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'

class ActiveSupport::TestCase

  def setup_with_global
    puts 'In Global setup'
    setup_without_global
  end

  alias_method_chain :setup, :global

end

class FooTest < ActiveSupport::TestCase

  def setup_without_global
    puts 'In Local setup'
  end

  def test_truth
    assert true
  end

end
Johm answered 15/11, 2009 at 11:5 Comment(3)
In this project it's necessary to clear some global memory caches, so that test cases don't pollute each other. I agree in general that this is a bit of a smell, but IMO it makes more sense here than requiring developers to remember to call a helper, and dealing with the sometime unpredictable consequences. I'm wondering if there's someway to avoid having to call super in each subclass that defines a setup. Does ActiveSupport::TestCase provide this feature with its setups/teardowns?Belay
I added to the original response - not sure if that solution gets you any closer.Johm
Without a doubt the best solution is to use the 'super' approach. Do not use alias_method_chain.Ellingston
B
24

Assuming you're using Rails. Just add following in your test/test_helper.rb file.

class ActiveSupport::TestCase
  setup :global_setup

  def global_setup
    #stuff to run before _every_ test.
  end
end

Tested on Rails 3.0.9.

Blanket answered 16/8, 2011 at 11:10 Comment(2)
Is there a way to make sure the local setup method is called first?Protest
@IsaacBetesh Nope, I don't think so. But, I'm pretty sure that you can find a workaround in your code as this is a pretty standard way of chaining - "common setup before local setup". (Though I'm not selling that as a "fact", I'm just saying that it is more of a rule than an exception; and there can be counter examples).Blanket
J
7

You could just patch Test::Unit::TestCase and define a setup method:

class Test::Unit::TestCase
  def setup
    puts 'in setup'
  end
end

And your subclasses would just use this by default:

class FooTest < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

class BarTest < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

If a test case needed to have its own setup, you would need to call super first to ensure that the global setup runs:

class BazTest < Test::Unit::TestCase
  def setup
    super
    puts 'custom setup'
  end

  def test_truth
    assert true
  end
end

Is having a global setup really something you need to do, or would it be helpful to have a helper method defined on Test::Unit::TestCase and call that in the tests that need it? The helper method approach is something that I find beneficial on my projects – the setup state and intention is clearer in each individual test and I don't need to jump around to find some "hidden" setup method. Quite often, a global setup is a code smell indicating that you need to rethink part of your design, but YMMV.

Update

Since you're using ActiveSupport, here's a first stab at something that won't require a call to super each time you define a setup method in your test case. I don't know how valuable it is, since it requires a call to a different method and any developer can just define their own setup method in the test case that will invalidate this change. Here it is:

require 'rubygems'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'

class ActiveSupport::TestCase

  def setup_with_global
    puts 'In Global setup'
    setup_without_global
  end

  alias_method_chain :setup, :global

end

class FooTest < ActiveSupport::TestCase

  def setup_without_global
    puts 'In Local setup'
  end

  def test_truth
    assert true
  end

end
Johm answered 15/11, 2009 at 11:5 Comment(3)
In this project it's necessary to clear some global memory caches, so that test cases don't pollute each other. I agree in general that this is a bit of a smell, but IMO it makes more sense here than requiring developers to remember to call a helper, and dealing with the sometime unpredictable consequences. I'm wondering if there's someway to avoid having to call super in each subclass that defines a setup. Does ActiveSupport::TestCase provide this feature with its setups/teardowns?Belay
I added to the original response - not sure if that solution gets you any closer.Johm
Without a doubt the best solution is to use the 'super' approach. Do not use alias_method_chain.Ellingston

© 2022 - 2024 — McMap. All rights reserved.