Class: Scope::TestCase

Inherits:
MiniTest::Unit::TestCase
  • Object
show all
Defined in:
lib/scope.rb

Overview

A test case class which provides nested contexts. Subclasses will have the “setup”, “teardown”, and “should” methods available as class methods.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.context(name, &block) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/scope.rb', line 34

def self.context(name, &block)
  parent = @contexts.last
  new_context = Context.new(name, parent)
  parent.tests_and_subcontexts << new_context
  @contexts << new_context
  block.call
  @contexts.pop
end

.context_for_testObject

A map of test name => Context.



8
# File 'lib/scope.rb', line 8

def self.context_for_test() @context_for_test end

.focusObject

“Focuses” the next test that’s defined after this method is called, ensuring that only that test is run.



67
68
69
70
71
72
73
74
# File 'lib/scope.rb', line 67

def self.focus
  # Since we're focusing only the next test, remove any tests which were already defined.
  context_for_test.values.uniq.each do |context|
    context.tests_and_subcontexts.reject! { |test| test.is_a?(String) }
  end
  @focus_enabled = true
  @focus_next_test = true
end

.inherited(subclass) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/scope.rb', line 10

def self.inherited(subclass)
  # Calling Unit::TestCase's inherited() method is important, as that's how it registers test suites.
  super

  subclass.instance_eval do
    # Pretend the whole test is wrapped in a context, so we can always code as if tests are in contexts.
    @contexts = [Context.new("")]
    @context_for_test = {}

    # The tests defined in this test case. MiniTest::Unit::TestCase sorts these methods randomly or
    # alphabetically. Let's run them in the order they were defined, as that's least surprising.
    def test_methods()
      tests = []
      stack = [@contexts.first]
      until stack.empty? do
        item = stack.pop
        stack += item.tests_and_subcontexts.reverse if item.is_a?(Context)
        tests << item if item.is_a?(String)
      end
      tests
    end
  end
end

.setup(&block) ⇒ Object



58
# File 'lib/scope.rb', line 58

def self.setup(&block) @contexts.last.setup= block end

.setup_once(&block) ⇒ Object

setup_once blocks are run just once for a context, and not on a per-test basis. They are useful for integration tests with costly setup.



63
# File 'lib/scope.rb', line 63

def self.setup_once(&block) @contexts.last.setup_once = block end

.should(name, &block) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/scope.rb', line 43

def self.should(name, &block)
  # When focus_enabled is true, we'll only be running the next should() block that gets defined.
  if @focus_enabled
    return unless @focus_next_test
    @focus_next_test = false
  end

  context_name = @contexts[1..-1].map { |context| context.name }.join(" ")
  context_name += " " unless context_name.empty?
  test_method_name = "#{context_name}should #{name}"
  define_method test_method_name, block
  @contexts.last.tests_and_subcontexts << test_method_name
  @context_for_test[test_method_name] = @contexts.last
end

.teardown(&block) ⇒ Object



59
# File 'lib/scope.rb', line 59

def self.teardown(&block) @contexts.last.teardown = block end

.teardown_once(&block) ⇒ Object



64
# File 'lib/scope.rb', line 64

def self.teardown_once(&block) @contexts.last.teardown_once = block end

Instance Method Details

#run(test_runner) ⇒ Object

run() is called by the MiniTest framework. This TestCase class is instantiated once per test method defined, and then run() is called on each test case instance.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/scope.rb', line 78

def run(test_runner)
  test_name = self.__name__
  context = self.class.context_for_test[test_name]
  result = nil
  begin
    # Unit::TestCase's implementation of run() invokes the test method with exception handling.
    context.run_setup_and_teardown(self, test_name) { result = super }
  rescue *MiniTest::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
    raise
  rescue Exception => error
    result = test_runner.puke(self.class, self.__name__, error)
  end
  result
end