class Test::Unit::TestCase

将所有内容联系在一起。如果你继承并添加自己的测试方法,它会负责将它们转换为测试,并将这些测试包装到一个套件中。它还会执行实际运行单个测试并将结果收集到 Test::Unit::TestResult 对象中的细节工作。

你可以在 TestCase 运行前后运行两个钩子。

示例

class TestMyClass < Test::Unit::TestCase
  class << self
    def startup
      ...
    end

    def shutdown
      ...
    end
  end

  def setup
    ...
  end

  def cleanup
    ...
  end

  def teardown
    ...
  end

  def test_my_method1
    ...
  end

  def test_my_method2
    ...
  end
end

以下是调用顺序

  1. startup

  2. setup

  3. test_my_method1

  4. cleanup

  5. teardown

  6. setup

  7. test_my_method2

  8. cleanup

  9. teardown

  10. shutdown

你可以为每个测试设置一个属性。

示例

class TestMyClass < Test::Unit::TestCase
  attribute :speed, :fast
  def test_my_fast_method
    # You can get the attribute via `self[]`
    self[:speed] # => :fast
    ...
  end

  attribute :speed, :slow
  def test_my_slow_method
    self[:speed] # => :slow
    ...
  end
end

属性

method_name[R]

公共类方法

description(value, target=nil) 点击切换源代码

描述一个测试。

以下示例将 “register a normal user” 描述与 “test_register” 测试关联。

description "register a normal user"
def test_register
  ...
end
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 374
def description(value, target=nil)
  targets = [target].compact
  attribute(:description, value, {}, *targets)
end
find_locations(query) 点击切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 466
def find_locations(query)
  query_path = query[:path]
  query_line = query[:line]
  query_method_name = query[:method_name]

  available_locations = target_method_locations(query_path)
  if query_line
    available_locations = available_locations.sort_by do |location|
      -location[:line]
    end
    available_location = available_locations.find do |location|
      query_line >= location[:line]
    end
    return [] if available_location.nil?
    return [] if available_location[:test_case] != self
    available_locations = [available_location]
  end
  if query_method_name
    available_location = available_locations.find do |location|
      location[:test_case] == self and
        query_method_name == location[:method_name]
    end
    return [] if available_location.nil?
    available_locations = [available_location]
  end

  available_locations
end
new(test_method_name) 点击切换源代码

创建一个新的测试夹具实例,用于运行由 test_method_name 表示的测试。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 553
def initialize(test_method_name)
  @method_name = test_method_name
  @internal_data = InternalData.new
end
parallel_safe?() 点击切换源代码

指示测试是否是并行安全的。

此方法返回 ‘false’ 的测试在并行安全测试运行之前按顺序执行。这仅在指定 `–parallel` 选项时有效。

@example 指示 test_parallel_unsafe 不是并行安全的

class TestMyClass < Test::Unit::TestCase
  class << self
    def parallel_safe?
      false
    end
  end

  def test_parallel_unsafe
    # ...
  end
end

@since 3.6.3

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 149
def parallel_safe?
  true
end
ractor(options={}) 点击切换源代码

声明以下测试使用 Ractor。

使用 Ractor 的测试在最后执行。因为多 Ractor 模式在当前进程中启用,并且即使在 Ruby 3.0 上运行使用 Ractor 的测试后只有一个 Ractor 运行时也不会禁用它。这个问题将来会解决。

这是通过将测试的 `:ractor` 属性设置为 `true` 实现的。

@param options [Hash] 请参阅 {Attribute::ClassMethods#attribute}

for details.

@return [void]

@example 声明 test_do_something_with_ractor 使用 Ractor

ractor
def test_do_something_with_ractor
  Ractor.new do
    # ...
  end
end

@since 3.4.6

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 405
def ractor(options={})
  attribute(:ractor, true, options)
end
shutdown() 点击切换源代码

在每个测试用例运行后调用。可用于拆卸测试用例范围内使用的夹具信息。

以下是一个示例测试用例

class TestMyClass < Test::Unit::TestCase
  class << self
    def shutdown
      ...
    end
  end

  def teardown
    ...
  end

  def test_my_class1
    ...
  end

  def test_my_class2
    ...
  end
end

以下是调用顺序

  • test_my_class1(或 test_my_class2)

  • teardown

  • test_my_class2(或 test_my_class1)

  • teardown

  • shutdown

请注意,你不应假设测试顺序。测试应按任何顺序工作。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 284
def shutdown
end
startup() 点击切换源代码

在每个测试用例运行之前调用。可用于设置测试用例范围内使用的夹具信息。

以下是一个示例测试用例

class TestMyClass < Test::Unit::TestCase
  class << self
    def startup
      ...
    end
  end

  def setup
    ...
  end

  def test_my_class1
    ...
  end

  def test_my_class2
    ...
  end
end

以下是调用顺序

  • startup

  • setup

  • test_my_class1(或 test_my_class2)

  • setup

  • test_my_class2(或 test_my_class1)

请注意,你不应假设测试顺序。测试应按任何顺序工作。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 246
def startup
end
sub_test_case(name, &block) 点击切换源代码

定义一个子测试用例。

这是一种语法糖。以下两种代码含义相同

标准

class TestParent < Test::Unit::TestCase
  class TestChild < self
    def test_in_child
    end
  end
end

语法糖

class TestParent < Test::Unit::TestCase
  sub_test_case("TestChild") do
    def test_in_child
    end
  end
end

它们的区别如下

  • {sub_test_case} 创建的 Test 用例是一个匿名类。因此,你不能通过名称引用测试用例。

  • 类风格的类名必须遵循 Ruby 中的常量命名规则。但是由 {sub_test_case} 创建的测试用例的名称不需要遵循该规则。例如,你可以在名称中使用空格,例如 “child test”。

@param name [String] 新创建的子测试用例的名称。@yield

The block is evaluated under the newly created sub test
case class context.

@return [Test::Unit::TestCase] 创建的子测试用例类。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 446
def sub_test_case(name, &block)
  sub_test_case = sub_test_case_class(name)
  sub_test_case.class_eval(&block)
  sub_test_case
end
suite() 点击切换源代码

将夹具中的所有 test* 方法汇总到一个套件中,为每个方法创建夹具的新实例。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 205
def suite
  suite_creator = TestSuiteCreator.new(self)
  suite_creator.create
end
test(*test_description_or_targets, &block) 点击切换源代码

以声明性语法定义测试,或将以下方法标记为测试方法。

在声明性语法用法中,以下两个测试定义几乎相同

description "register user"
def test_register_user
  ...
end

test "register user" do
  ...
end

在测试方法标记用法中,“my_test_method” 被视为测试方法

test
def my_test_method
  assert_equal("call me", ...)
end
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 337
def test(*test_description_or_targets, &block)
  if block_given?
    test_description = test_description_or_targets.first
    if test_description.nil?
      raise ArgumentError, "test description is missing"
    end
    n_arguments = test_description_or_targets.size
    if n_arguments > 1
      message = "wrong number of arguments (#{n_arguments} for 1)"
      raise ArgumentError, message
    end
    method_name = "test: #{test_description}"
    description(test_description, method_name)
    attribute(:test, true, {}, method_name)
    if block.respond_to?(:source_location)
      attribute(:source_location, block.source_location, {}, method_name)
    end
    define_method(method_name, &block)
  else
    targets = test_description_or_targets
    attribute(:test, true, {}, *targets)
    targets.each do |target|
      AutoRunnerLoader.check(self, target)
    end
  end
end
test_defined?(query) 点击切换源代码

检查是否定义了与查询匹配的测试。

@option query [String] :path (nil)

the path where a test is defined in.

@option query [Numeric] :line (nil)

the line number where a test is defined at.

@option query [String] :method_name (nil)

the method name for a test.
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 461
def test_defined?(query)
  locations = find_locations(query)
  not locations.empty?
end
test_order() 点击切换源代码

返回当前的测试顺序。默认情况下返回 `:alphabetic`。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 291
def test_order
  ancestors.each do |ancestor|
    order = @@test_orders[ancestor]
    return order if order
  end
  AVAILABLE_ORDERS.first
end
test_order=(order) 点击切换源代码

设置当前的测试顺序。

以下是可用的 _order_

:alphabetic:默认值。测试按字母顺序排序。

:random:测试按随机顺序排序。

:defined:测试按定义的顺序排序。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 311
def test_order=(order)
  @@test_orders[self] = order
end

私有类方法

add_method_location(location) 点击切换源代码

@private

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 507
def add_method_location(location)
  @@method_location_mutex.synchronize do
    method_locations << location
  end
end
method_locations() 点击切换源代码

@private

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 502
def method_locations
  @@method_locations[self] ||= []
end
sub_test_case_class(name) 点击切换源代码

@private

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 538
def sub_test_case_class(name)
  parent_test_case = self
  Class.new(self) do
    singleton_class = class << self; self; end
    singleton_class.__send__(:define_method, :name) do
      [parent_test_case.name, name].compact.join("::")
    end
  end
end
target_method_locations(path) 点击切换源代码

@private

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 514
def target_method_locations(path)
  @@method_location_mutex.synchronize do
    if path.nil?
      self_location = method_locations.first
      path = self_location[:path] if self_location
    end
    return [] if path.nil?

    target_locations = []
    @@method_locations.each do |test_case, locations|
      locations.each do |location|
        absolete_path = File.expand_path(path)
        location_path = location[:path]
        location_basename = File.basename(location_path)
        if location_path == absolete_path or location_basename == path
          target_locations << location.merge(:test_case => test_case)
        end
      end
    end
    target_locations
  end
end

公共实例方法

==(other) 点击切换源代码

能够比较 TestCase 实例很方便。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 819
def ==(other)
  return false unless other.kind_of?(self.class)
  return false unless @method_name == other.method_name
  return false unless data_label == other.data_label
  self.class == other.class
end
add_pass() 点击切换源代码

通知测试已通过。通常,不需要这样做,因为 run 会自动调用它。如果你想覆盖 run,这不是一个好主意。请联系 test-unit 开发人员。我们将在没有你的自定义 run 的情况下帮助你。例如,我们可能会在 run 中添加一个新的钩子。

这是为扩展 test-unit 的开发人员提供的公共 API。

@return [void]

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 868
def add_pass
  current_result.add_pass
end
cleanup() 点击切换源代码

在每个测试方法运行后调用,但测试方法未标记为 “passed”。可用于清理和/或验证测试的条件。例如,可用于验证模拟。

你可以通过以下代码添加额外的清理任务

class TestMyClass < Test::Unit::TestCase
  def cleanup
    ...
  end

  cleanup
  def my_cleanup1
    ...
  end

  cleanup do
    ... # cleanup callback1
  end

  cleanup
  def my_cleanup2
    ...
  end

  cleanup do
    ... # cleanup callback2
  end

  def test_my_class
    ...
  end
end

以下是调用顺序

  • test_my_class

  • cleanup callback2

  • my_cleanup2

  • cleanup callback1

  • my_cleanup1

  • cleanup

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 716
def cleanup
end
data() 点击切换源代码

返回测试的测试数据。如果测试未与任何测试数据关联,则返回 `nil`。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 781
def data
  @internal_data.test_data
end
data_label() 点击切换源代码

返回测试的测试数据标签。如果测试未与任何测试数据关联,则返回 `nil`。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 775
def data_label
  @internal_data.test_data_label
end
default_test() 点击切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 764
def default_test
  flunk("No tests were specified")
end
description() 点击切换源代码

返回测试的描述。描述将通过 Test::Unit::TestCase.testTest::Unit::TestCase.description 关联。

对于没有描述的测试,返回测试的名称。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 809
def description
  self[:description] || name
end
elapsed_time() 点击切换源代码

返回测试运行的耗时。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 832
def elapsed_time
  @internal_data.elapsed_time
end
interrupted?() 点击切换源代码

返回测试是否被中断。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 837
def interrupted?
  @internal_data.interrupted?
end
local_name() 点击切换源代码

返回此 TestCase 实例所代表的特定测试的人类可读名称。

`#local_name` 不包括类名。`#name` 包括类名。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 796
def local_name
  if @internal_data.have_test_data?
    "#{@method_name}[#{data_label}]"
  else
    @method_name.to_s
  end
end
name() 点击切换源代码

返回此 TestCase 实例所代表的特定测试的人类可读名称。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 787
def name
  "#{local_name}(#{self.class.name})"
end
passed?() 点击切换源代码

返回此单独测试是否通过。主要用于拆卸,以便在测试失败时可以留下工件。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 844
def passed?
  @internal_data.passed?
end
problem_occurred() 点击切换源代码

通知测试中发生了问题。这意味着该测试是失败的测试。如果测试套件中存在任何失败的测试,则测试过程将以失败的退出状态退出。

这是为扩展 test-unit 的开发人员提供的公共 API。

@return [void]

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 855
def problem_occurred
  @internal_data.problem_occurred
end
run(result, runner_class: nil) { |STARTED, name| ... } 点击切换源代码

运行此夹具实例所表示的单个测试方法,在结果中收集统计信息、失败和错误。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 580
def run(result, runner_class: nil)
  begin
    @_result = result
    @internal_data.test_started
    yield(STARTED, name)
    yield(STARTED_OBJECT, self)
    processed_exception_in_setup = false
    begin
      catch do |tag|
        run_setup do
          begin
            run_test
            run_cleanup
            add_pass
          rescue Exception
            @internal_data.interrupted
            unless handle_exception($!)
              processed_exception_in_setup = true
              raise
            end
            throw(tag)
          end
        end
      end
    rescue Exception
      if processed_exception_in_setup
        raise
      else
        @internal_data.interrupted
        raise unless handle_exception($!)
      end
    ensure
      begin
        run_teardown
      rescue Exception
        raise unless handle_exception($!)
      end
    end
    @internal_data.test_finished
    result.add_run
    yield(FINISHED, name)
    yield(FINISHED_OBJECT, self)
  ensure
    # @_result = nil # For test-spec's after_all :<
  end
end
setup() 点击切换源代码

在每个测试方法运行之前调用。可用于设置夹具信息。

你可以通过以下代码添加额外的设置任务

class TestMyClass < Test::Unit::TestCase
  def setup
    ...
  end

  setup
  def my_setup1
    ...
  end

  setup do
    ... # setup callback1
  end

  setup
  def my_setup2
    ...
  end

  setup do
    ... # setup callback2
  end

  def test_my_class
    ...
  end
end

以下是调用顺序

  • setup

  • my_setup1

  • setup callback1

  • my_setup2

  • setup callback2

  • test_my_class

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 669
def setup
end
size() 点击切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 768
def size
  1
end
start_time() 点击切换源代码

返回测试开始时的时间。

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 827
def start_time
  @internal_data.start_time
end
teardown() 点击切换源代码

在每个测试方法运行后调用。可用于清理测试装置信息。

您可以通过以下代码添加额外的清理任务

class TestMyClass < Test::Unit::TestCase
  def teardown
    ...
  end

  teardown
  def my_teardown1
    ...
  end

  teardown do
    ... # teardown callback1
  end

  teardown
  def my_teardown2
    ...
  end

  teardown do
    ... # teardown callback2
  end

  def test_my_class
    ...
  end
end

以下是调用顺序

  • test_my_class

  • 清理回调2

  • my_teardown2

  • 清理回调1

  • my_teardown1

  • teardown

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 761
def teardown
end
to_s() 点击以切换源代码

重写以返回 name

# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 814
def to_s
  name
end

私有实例方法

add_assertion() 点击以切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 912
def add_assertion
  current_result.add_assertion
end
current_result() 点击以切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 873
def current_result
  @_result
end
handle_exception(exception) 点击以切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 900
def handle_exception(exception)
  self.class.exception_handlers.each do |handler|
    if handler.respond_to?(:call)
      handled = handler.call(self, exception)
    else
      handled = __send__(handler, exception)
    end
    return true if handled
  end
  false
end
run_test() 点击以切换源代码
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 877
def run_test
  signature = "#{self.class}\##{@method_name}"
  redefined_info = self[:redefined]
  if redefined_info
    notify("<#{signature}> was redefined",
           :backtrace => redefined_info[:backtrace])
  end
  if self[:ractor] and not defined?(::Ractor)
    omit("<#{signature}> requires Ractor")
  end
  if @internal_data.have_test_data?
    test_method = method(@method_name)
    arity = test_method.arity
    if arity.zero?
      __send__(@method_name)
    else
      __send__(@method_name, @internal_data.test_data)
    end
  else
    __send__(@method_name)
  end
end