模块 Minitest::Assertions
Minitest
Assertions
。所有断言方法都接受一个 msg
,如果断言失败,则会打印该 msg
。
协议:这里几乎所有内容都归结为 assert
,它期望能够递增一个名为 assertions
的实例访问器。这不由 Assertions
提供,而必须由包含 Assertions
的内容提供。有关示例,请参阅 Minitest::Runnable。
公共类方法
返回要在 diff
中使用的 diff 命令。尝试智能地找出要使用的 diff 命令。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 27 def self.diff return @diff if defined? @diff @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ and system "diff.exe", __FILE__, __FILE__) then "diff.exe -u" elsif system "gdiff", __FILE__, __FILE__ then "gdiff -u" # solaris and kin suck elsif system "diff", __FILE__, __FILE__ then "diff -u" else nil end end
设置要在 diff
中使用的 diff 命令。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 45 def self.diff= o @diff = o end
公共实例方法
除非 test
为真值,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 171 def assert test, msg = nil self.assertions += 1 unless test then msg ||= "Expected #{mu_pp test} to be truthy." msg = msg.call if Proc === msg raise Minitest::Assertion, msg end true end
除非 obj
为空,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 188 def assert_empty obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to be empty" } assert_respond_to obj, :empty? assert obj.empty?, msg end
除非 exp == act
,否则失败,如果可能,则打印两者之间的差异。
如果没有可见的差异但断言失败,您应该怀疑您的 == 有错误,或者您的 inspect 输出遗漏了关键细节。 为了获得更漂亮的结构化差异,请设置 Minitest::Test.make_my_diffs_pretty!
对于浮点数,请使用 assert_in_delta。
另请参阅:Minitest::Assertions.diff
# File minitest-5.25.4/lib/minitest/assertions.rb, line 214 def assert_equal exp, act, msg = nil msg = message(msg, E) { diff exp, act } result = assert exp == act, msg if nil == exp then if Minitest::VERSION >= "6" then refute_nil exp, "Use assert_nil if expecting nil." else warn "DEPRECATED: Use assert_nil if expecting nil from #{_where}. This will fail in Minitest 6." end end result end
用于比较浮点数。 除非 exp
和 act
彼此相差在 delta
内,否则失败。
assert_in_delta Math::PI, (22.0 / 7.0), 0.01
# File minitest-5.25.4/lib/minitest/assertions.rb, line 235 def assert_in_delta exp, act, delta = 0.001, msg = nil n = (exp - act).abs msg = message(msg) { "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}" } assert delta >= n, msg end
用于比较浮点数。 除非 exp
和 act
的相对误差小于 epsilon
,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 247 def assert_in_epsilon exp, act, epsilon = 0.001, msg = nil assert_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg end
除非 collection
包含 obj
,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 254 def assert_includes collection, obj, msg = nil msg = message(msg) { "Expected #{mu_pp collection} to include #{mu_pp obj}" } assert_respond_to collection, :include? assert collection.include?(obj), msg end
除非 obj
是 cls
的实例,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 265 def assert_instance_of cls, obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to be an instance of #{cls}, not #{obj.class}" } assert obj.instance_of?(cls), msg end
除非 obj
是 cls
的一种,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 276 def assert_kind_of cls, obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to be a kind of #{cls}, not #{obj.class}" } assert obj.kind_of?(cls), msg end
除非 matcher
=~
obj
,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 287 def assert_match matcher, obj, msg = nil msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" } assert_respond_to matcher, :=~ matcher = Regexp.new Regexp.escape matcher if String === matcher assert matcher =~ obj, msg Regexp.last_match end
断言模拟正确验证,如果未验证则失败。
# File minitest-5.25.4/lib/minitest/mock.rb, line 253 def assert_mock mock, msg = nil assert mock.verify rescue MockExpectationError => e msg = message(msg) { e.message } flunk msg end
除非 obj
为 nil,否则失败
# File minitest-5.25.4/lib/minitest/assertions.rb, line 299 def assert_nil obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to be nil" } assert obj.nil?, msg end
用于使用二元运算符进行测试。例如
assert_operator 5, :<=, 4
# File minitest-5.25.4/lib/minitest/assertions.rb, line 309 def assert_operator o1, op, o2 = UNDEFINED, msg = nil return assert_predicate o1, op, msg if UNDEFINED == o2 msg = message(msg) { "Expected #{mu_pp o1} to be #{op} #{mu_pp o2}" } assert o1.__send__(op, o2), msg end
如果 stdout 或 stderr 未输出预期结果,则失败。 如果您不关心该流的输出,请传入 nil。 如果您要求它保持静默,请传入 “”。 如果您想进行模式匹配,请传入正则表达式。
assert_output(/hey/) { method_with_output }
注意:这使用 capture_io
,而不是 capture_subprocess_io
。
另请参阅:assert_silent
# File minitest-5.25.4/lib/minitest/assertions.rb, line 327 def assert_output stdout = nil, stderr = nil flunk "assert_output requires a block to capture output." unless block_given? out, err = capture_io do yield end err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout y = send err_msg, stderr, err, "In stderr" if err_msg x = send out_msg, stdout, out, "In stdout" if out_msg (!stdout || x) && (!stderr || y) rescue Assertion raise rescue => e raise UnexpectedError, e end
除非 path
存在,否则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 351 def assert_path_exists path, msg = nil msg = message(msg) { "Expected path '#{path}' to exist" } assert File.exist?(path), msg end
用于使用模式匹配进行测试(仅在 Ruby 3.0 及更高版本中受支持)
# pass assert_pattern { [1,2,3] => [Integer, Integer, Integer] } # fail "length mismatch (given 3, expected 1)" assert_pattern { [1,2,3] => [Integer] }
裸 =>
模式在失败时会引发 NoMatchingPatternError,这通常会被计为测试错误。此断言会捕获 NoMatchingPatternError 并生成测试失败。任何其他异常都将照常引发并生成测试错误。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 370 def assert_pattern raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0" flunk "assert_pattern requires a block to capture errors." unless block_given? begin # TODO: remove after ruby 2.6 dropped yield pass rescue NoMatchingPatternError => e flunk e.message end end
用于使用谓词进行测试。例如
assert_predicate str, :empty?
这实际上是为规范而设计的,并且由 assert_operator
前端化。
str.must_be :empty?
# File minitest-5.25.4/lib/minitest/assertions.rb, line 391 def assert_predicate o1, op, msg = nil msg = message(msg) { "Expected #{mu_pp o1} to be #{op}" } assert o1.__send__(op), msg end
除非块引发 exp
中的一个,否则失败。 返回匹配的异常,以便您可以检查消息、属性等。
exp
在末尾接受一个可选消息,以帮助解释失败,如果未传递异常类,则默认为 StandardError。例如
assert_raises(CustomError) { method_with_custom_error }
使用自定义错误消息
assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error }
使用返回的对象
error = assert_raises(CustomError) do raise CustomError, 'This is really bad' end assert_equal 'This is really bad', error.message
# File minitest-5.25.4/lib/minitest/assertions.rb, line 418 def assert_raises *exp flunk "assert_raises requires a block to capture errors." unless block_given? msg = "#{exp.pop}.\n" if String === exp.last exp << StandardError if exp.empty? begin yield rescue *exp => e pass # count assertion return e rescue Minitest::Assertion # incl Skip & UnexpectedError # don't count assertion raise rescue SignalException, SystemExit raise rescue Exception => e flunk proc { exception_details(e, "#{msg}#{mu_pp exp} exception expected, not") } end exp = exp.first if exp.size == 1 flunk "#{msg}#{mu_pp exp} expected but nothing was raised." end
除非 obj
响应 meth
,否则失败。 include_all 默认为 false 以匹配 Object#respond_to?。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 450 def assert_respond_to obj, meth, msg = nil, include_all: false msg = message(msg) { "Expected #{mu_pp obj} (#{obj.class}) to respond to ##{meth}" } assert obj.respond_to?(meth, include_all), msg end
除非 exp
和 act
相等,否则失败?
# File minitest-5.25.4/lib/minitest/assertions.rb, line 460 def assert_same exp, act, msg = nil msg = message(msg) { data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data } assert exp.equal?(act), msg end
send_ary
是一个接收者、消息和参数。
除非调用返回真值,否则失败
# File minitest-5.25.4/lib/minitest/assertions.rb, line 473 def assert_send send_ary, m = nil warn "DEPRECATED: assert_send. From #{_where}" recv, msg, *args = send_ary m = message(m) { "Expected #{mu_pp recv}.#{msg}(*#{mu_pp args}) to return true" } assert recv.__send__(msg, *args), m end
如果块向 stderr 或 stdout 输出任何内容,则失败。
另请参阅:assert_output
# File minitest-5.25.4/lib/minitest/assertions.rb, line 488 def assert_silent assert_output "", "" do yield end end
除非块抛出 sym
,否则失败
# File minitest-5.25.4/lib/minitest/assertions.rb, line 497 def assert_throws sym, msg = nil default = "Expected #{mu_pp sym} to have been thrown" caught = true value = catch sym do begin yield rescue ThreadError => e # wtf?!? 1.8 + threads == suck default += ", not :#{e.message[/uncaught throw \`(\w+?)\'/, 1]}" rescue ArgumentError => e # 1.9 exception raise e unless e.message.include? "uncaught throw" default += ", not #{e.message.split(/ /).last}" rescue NameError => e # 1.8 exception raise e unless e.name == sym default += ", not #{e.name.inspect}" end caught = false end assert caught, message(msg) { default } value rescue Assertion raise rescue => e raise UnexpectedError, e end
将 $stdout 和 $stderr 捕获到字符串中
out, err = capture_io do puts "Some info" warn "You did a bad thing" end assert_match %r%info%, out assert_match %r%bad%, err
注意:为了提高效率,此方法使用 StringIO,并且不捕获子进程的 IO。 请使用 capture_subprocess_io
。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 538 def capture_io _synchronize do begin captured_stdout, captured_stderr = StringIO.new, StringIO.new orig_stdout, orig_stderr = $stdout, $stderr $stdout, $stderr = captured_stdout, captured_stderr yield return captured_stdout.string, captured_stderr.string ensure $stdout = orig_stdout $stderr = orig_stderr end end end
将 $stdout 和 $stderr 捕获到字符串中,使用 Tempfile 以确保也捕获子进程 IO。
out, err = capture_subprocess_io do system "echo Some info" system "echo You did a bad thing 1>&2" end assert_match %r%info%, out assert_match %r%bad%, err
注意:此方法比 capture_io
慢大约 10 倍,因此仅在需要测试子进程的输出时使用它。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 571 def capture_subprocess_io _synchronize do begin require "tempfile" captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err") orig_stdout, orig_stderr = $stdout.dup, $stderr.dup $stdout.reopen captured_stdout $stderr.reopen captured_stderr yield $stdout.rewind $stderr.rewind return captured_stdout.read, captured_stderr.read ensure $stdout.reopen orig_stdout $stderr.reopen orig_stderr orig_stdout.close orig_stderr.close captured_stdout.close! captured_stderr.close! end end end
返回 exp
和 act
之间的差异。 如果没有已知的 diff 命令,或者 diff 输出没有意义(单行、短输出),则它只是返回两者之间的基本比较。
有关更多信息,请参阅 things_to_diff
。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 57 def diff exp, act result = nil expect, butwas = things_to_diff exp, act return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless expect Tempfile.open "expect" do |a| a.puts expect a.flush Tempfile.open "butwas" do |b| b.puts butwas b.flush result = `#{Minitest::Assertions.diff} #{a.path} #{b.path}` result.sub!(/^\-\-\- .+/, "--- expected") result.sub!(/^\+\+\+ .+/, "+++ actual") if result.empty? then klass = exp.class result = [ "No visible difference in the #{klass}#inspect output.\n", "You should look at the implementation of #== on ", "#{klass} or its members.\n", expect, ].join end end end result end
返回异常 e
的详细信息
# File minitest-5.25.4/lib/minitest/assertions.rb, line 603 def exception_details e, msg [ msg, "Class: <#{e.class}>", "Message: <#{e.message.inspect}>", "---Backtrace---", Minitest.filter_backtrace(e.backtrace), "---------------", ].join "\n" end
在给定日期(在当地时区)之后失败。 如果您需要保留某些内容直到稍后日期以免忘记它,这允许您在测试中放置时间炸弹。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 619 def fail_after y, m, d, msg flunk msg if Time.now > Time.local(y, m, d) end
使用 msg
失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 626 def flunk msg = nil msg ||= "Epic Fail!" assert false, msg end
返回一个 proc,它将输出 msg
以及默认消息。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 634 def message msg = nil, ending = nil, &default proc { msg = msg.call.chomp(".") if Proc === msg custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? "#{custom_message}#{default.call}#{ending || "."}" } end
这将返回 obj
的人类可读版本。 默认情况下,会调用 inspect。 如果需要,您可以覆盖它以使用 pretty_inspect。
请参阅 Minitest::Test.make_my_diffs_pretty!
# File minitest-5.25.4/lib/minitest/assertions.rb, line 127 def mu_pp obj s = obj.inspect.encode Encoding.default_external return s unless String === obj && (obj.encoding != Encoding.default_external || !obj.valid_encoding?) enc = "# encoding: #{obj.encoding}" val = "# valid: #{obj.valid_encoding?}" [enc, val, s].join "\n" end
这将返回 obj
的可区分、更人性化的版本。 这与常规的 mu_pp
不同,因为它扩展了转义的换行符并使十六进制值(如 object_ids)通用化。 这使用 mu_pp
执行第一次传递,然后对其进行清理。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 145 def mu_pp_for_diff obj str = mu_pp obj # both '\n' & '\\n' (_after_ mu_pp (aka inspect)) single = str.match?(/(?<!\\|^)\\n/) double = str.match?(/(?<=\\|^)\\n/) process = if single ^ double then if single then lambda { |s| s == "\\n" ? "\n" : s } # unescape else lambda { |s| s == "\\\\n" ? "\\n\n" : s } # unescape a bit, add nls end else :itself # leave it alone end str .gsub(/\\?\\n/, &process) .gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values end
用于计算断言
# File minitest-5.25.4/lib/minitest/assertions.rb, line 645 def pass _msg = nil assert true end
如果 test
为真值,则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 652 def refute test, msg = nil msg ||= message { "Expected #{mu_pp test} to not be truthy" } assert !test, msg end
如果 obj
为空,则失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 660 def refute_empty obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to not be empty" } assert_respond_to obj, :empty? refute obj.empty?, msg end
如果 exp == act
,则失败。
对于浮点数,请使用 refute_in_delta。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 671 def refute_equal exp, act, msg = nil msg = message(msg) { "Expected #{mu_pp act} to not be equal to #{mu_pp exp}" } refute exp == act, msg end
用于比较浮点数。 如果 exp
在 act
的 delta
范围内,则失败。
refute_in_delta Math::PI, (22.0 / 7.0)
# File minitest-5.25.4/lib/minitest/assertions.rb, line 683 def refute_in_delta exp, act, delta = 0.001, msg = nil n = (exp - act).abs msg = message(msg) { "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}" } refute delta >= n, msg end
用于比较浮点数。如果 exp
和 act
的相对误差小于 epsilon
,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 695 def refute_in_epsilon a, b, epsilon = 0.001, msg = nil refute_in_delta a, b, a * epsilon, msg end
如果 collection
包含 obj
,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 702 def refute_includes collection, obj, msg = nil msg = message(msg) { "Expected #{mu_pp collection} to not include #{mu_pp obj}" } assert_respond_to collection, :include? refute collection.include?(obj), msg end
如果 obj
是 cls
的一个实例,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 713 def refute_instance_of cls, obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to not be an instance of #{cls}" } refute obj.instance_of?(cls), msg end
如果 obj
是 cls
的一种,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 723 def refute_kind_of cls, obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to not be a kind of #{cls}" } refute obj.kind_of?(cls), msg end
如果 matcher
=~
obj
,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 731 def refute_match matcher, obj, msg = nil msg = message(msg) { "Expected #{mu_pp matcher} to not match #{mu_pp obj}" } assert_respond_to matcher, :=~ matcher = Regexp.new Regexp.escape matcher if String === matcher refute matcher =~ obj, msg end
如果 obj
为 nil,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 741 def refute_nil obj, msg = nil msg = message(msg) { "Expected #{mu_pp obj} to not be nil" } refute obj.nil?, msg end
如果 o1
不是 op
o2
,则断言失败。例如:
refute_operator 1, :>, 2 #=> pass refute_operator 1, :<, 2 #=> fail
# File minitest-5.25.4/lib/minitest/assertions.rb, line 776 def refute_operator o1, op, o2 = UNDEFINED, msg = nil return refute_predicate o1, op, msg if UNDEFINED == o2 msg = message(msg) { "Expected #{mu_pp o1} to not be #{op} #{mu_pp o2}" } refute o1.__send__(op, o2), msg end
如果 path
存在,则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 785 def refute_path_exists path, msg = nil msg = message(msg) { "Expected path '#{path}' to not exist" } refute File.exist?(path), msg end
用于使用模式匹配进行测试(仅在 Ruby 3.0 及更高版本中受支持)
# pass refute_pattern { [1,2,3] => [String] } # fail "NoMatchingPatternError expected, but nothing was raised." refute_pattern { [1,2,3] => [Integer, Integer, Integer] }
此断言期望抛出 NoMatchingPatternError 异常,如果没有抛出异常,则断言失败。任何其他异常将正常抛出并产生测试错误。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 758 def refute_pattern raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0" flunk "refute_pattern requires a block to capture errors." unless block_given? begin yield flunk "NoMatchingPatternError expected, but nothing was raised." rescue NoMatchingPatternError pass end end
用于使用谓词进行测试。
refute_predicate str, :empty?
这实际上是为规范而设计的,并且由 refute_operator
前端化。
str.wont_be :empty?
# File minitest-5.25.4/lib/minitest/assertions.rb, line 799 def refute_predicate o1, op, msg = nil msg = message(msg) { "Expected #{mu_pp o1} to not be #{op}" } refute o1.__send__(op), msg end
如果 obj
响应消息 meth
,则断言失败。 include_all 默认为 false,以匹配 Object#respond_to?
# File minitest-5.25.4/lib/minitest/assertions.rb, line 808 def refute_respond_to obj, meth, msg = nil, include_all: false msg = message(msg) { "Expected #{mu_pp obj} to not respond to #{meth}" } refute obj.respond_to?(meth, include_all), msg end
如果 exp
与 act
相同(通过对象标识),则断言失败。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 817 def refute_same exp, act, msg = nil msg = message(msg) { data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data } refute exp.equal?(act), msg end
跳过当前运行。如果在详细模式下运行,跳过的运行会在运行结束时列出,但不会导致失败的退出代码。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 830 def skip msg = nil, _ignored = nil msg ||= "Skipped, no message given" @skip = true raise Minitest::Skip, msg end
跳过当前运行直到给定日期(在本地时区)。这允许您将一些修复暂时搁置到稍后的日期,但仍然让您负责并防止您忘记它。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 842 def skip_until y, m, d, msg skip msg if Time.now < Time.local(y, m, d) where = caller(1..1).first.rpartition(":in").reject(&:empty?).first warn "Stale skip_until %p at %s" % [msg, where] end
此测试用例是否被跳过?用于拆卸。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 851 def skipped? defined?(@skip) and @skip end
返回要进行差异比较的内容 [expect, butwas],如果没有任何内容要进行差异比较,则返回 [nil, nil]。
标准
-
字符串包含换行符或转义的换行符,但不能两者都包含。
-
或者:字符串长度 > 30 个字符。
-
或者:字符串彼此相等(但可能是不同的编码?)。
-
并且:我们找到了一个 diff 可执行文件。
# File minitest-5.25.4/lib/minitest/assertions.rb, line 102 def things_to_diff exp, act expect = mu_pp_for_diff exp butwas = mu_pp_for_diff act e1, e2 = expect.include?("\n"), expect.include?("\\n") b1, b2 = butwas.include?("\n"), butwas.include?("\\n") need_to_diff = (e1 ^ e2 || b1 ^ b2 || expect.size > 30 || butwas.size > 30 || expect == butwas) && Minitest::Assertions.diff need_to_diff && [expect, butwas] end