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
以下是调用顺序
-
startup
-
setup
-
test_my_method1
-
cleanup
-
teardown
-
setup
-
test_my_method2
-
cleanup
-
teardown
-
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
属性
公共类方法
描述一个测试。
以下示例将 “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
# 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
创建一个新的测试夹具实例,用于运行由 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
指示测试是否是并行安全的。
此方法返回 ‘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。
使用 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
在每个测试用例运行后调用。可用于拆卸测试用例范围内使用的夹具信息。
以下是一个示例测试用例
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
在每个测试用例运行之前调用。可用于设置测试用例范围内使用的夹具信息。
以下是一个示例测试用例
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
定义一个子测试用例。
这是一种语法糖。以下两种代码含义相同
标准
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
将夹具中的所有 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
以声明性语法定义测试,或将以下方法标记为测试方法。
在声明性语法用法中,以下两个测试定义几乎相同
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
检查是否定义了与查询匹配的测试。
@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
返回当前的测试顺序。默认情况下返回 `: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
设置当前的测试顺序。
以下是可用的 _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
私有类方法
@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
@private
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 502 def method_locations @@method_locations[self] ||= [] end
@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
@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
公共实例方法
能够比较 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
在每个测试方法运行后调用,但测试方法未标记为 “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
返回测试的测试数据。如果测试未与任何测试数据关联,则返回 `nil`。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 781 def data @internal_data.test_data end
返回测试的测试数据标签。如果测试未与任何测试数据关联,则返回 `nil`。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 775 def data_label @internal_data.test_data_label end
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 764 def default_test flunk("No tests were specified") end
返回测试的描述。描述将通过 Test::Unit::TestCase.test
或 Test::Unit::TestCase.description
关联。
对于没有描述的测试,返回测试的名称。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 809 def description self[:description] || name end
返回测试运行的耗时。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 832 def elapsed_time @internal_data.elapsed_time end
返回测试是否被中断。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 837 def interrupted? @internal_data.interrupted? end
返回此 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
返回此 TestCase
实例所代表的特定测试的人类可读名称。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 787 def name "#{local_name}(#{self.class.name})" end
返回此单独测试是否通过。主要用于拆卸,以便在测试失败时可以留下工件。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 844 def passed? @internal_data.passed? end
通知测试中发生了问题。这意味着该测试是失败的测试。如果测试套件中存在任何失败的测试,则测试过程将以失败的退出状态退出。
这是为扩展 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
运行此夹具实例所表示的单个测试方法,在结果中收集统计信息、失败和错误。
# 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
在每个测试方法运行之前调用。可用于设置夹具信息。
你可以通过以下代码添加额外的设置任务
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
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 768 def size 1 end
返回测试开始时的时间。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 827 def start_time @internal_data.start_time end
在每个测试方法运行后调用。可用于清理测试装置信息。
您可以通过以下代码添加额外的清理任务
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
重写以返回 name
。
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 814 def to_s name end
私有实例方法
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 912 def add_assertion current_result.add_assertion end
# File test-unit-3.6.7/lib/test/unit/testcase.rb, line 873 def current_result @_result end
# 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
# 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