deivid
deivid

Reputation: 5268

Minitest specs: multiple before/end blocks

This a minimal generic example of the situation I have:

test/mytest.rb

describe "What I want to test" do
  include TestUtils

  temporary_change_value(settings, :tmp_setting, value)

  describe "test1 blablabla" do
    # ...
  end

  describe "test2 blablabla" do
    # ...
  end
end

test/support/test_utils.rb

module TestUtils

  def self.included(base)
    base.class_eval do
      extend ClassMethods

      before do
        MyMainClass.interface = TestInterface.new
        MyMainClass.handler.display.clear
      end

      after do
        MyMainClass.handler.display.clear
      end
    end
  end

  module ClassMethods

    def temporary_change_value(item, key, value)
      old_value = nil

      before do
        old_value = item[key]
        item[key] = value
      end

      after do
        item[key] = old_value
      end
    end

  end
end

The TestUtils module is included in evey test file so the common before and end tasks are always executed, but in this case I also need to use the method temporary_change_value as a before block for all the tests in this file. This is not working, it seems that only one of the before/end blocks gets executed (specifically the one that sets MyMainClass.interface)

Things I've tried

I've tried to change the order of the before/end blocks, like this.

describe "What I want to test" do
  extend TestUtils::ClassMethods

  temporary_change_value(settings, :tmp_setting, value)

  include TestUtils

  describe "test1 blablabla" do
    # ...
  end

  describe "test2 blablabla" do
    # ...
  end
end

Still not working. Now the settings value doesn't get changed, so it seems that it is the second before/end block included the only one that gets executed.

The question: How can I achieve this and still keep it DRY?

Upvotes: 3

Views: 1299

Answers (2)

deivid
deivid

Reputation: 5268

I finally managed to solve this thanks to the mantainer of the Minitest gem @zenspider.

test/my_test.rb

class TestWhatIWant < TestUtils::TestCase
  temporary_change_value settings, :tmp_setting, value

  describe "test1 blablabla" do
    # ...
  end

  describe "test2 blablabla" do
    # ...
  end
end

test/support/test_utils.rb

module TestUtils
  module ClassUtils

    def temporary_change_value item, key, value
      mod = Module.new do
        extend Minitest::Spec::DSL

        before do
          @old_value = item[key]
          item[key] = value
        end

        after do
          item[key] = @old_value
        end
      end

      include mod
    end

    class TestCase < Minitest::Spec
      extend TestUtils::ClassUtils
      include TestUtils

      def setup
        Byebug.interface = TestInterface.new
        Byebug.handler.display.clear
      end
    end

  end
end

Upvotes: 1

user2246711
user2246711

Reputation: 21

Since MiniTest 3.3, before/end are just methods, not hooks, so when you call before do ... end, you are overwriting what used to be your before method. What you can use instead is MiniTest::TestCase.add_setup_hook (http://bfts.rubyforge.org/minitest/MiniTest/Unit/TestCase.html#method-c-add_setup_hook). You should be able to put the code from temporary_change_value() inside an add_setup_hook block in that particular test case to achieve what you're looking for.

Upvotes: 2

Related Questions