AnyMath 文档

单元测试

测试基地Julia

Julia正在快速开发中,并拥有一个广泛的测试套件来验证跨多个平台的功能。 如果您从源代码构建Julia,则可以使用以下方法运行此测试套件 进行测试. 在二进制安装中,您可以使用以下方法运行测试套件 基地。符咒().

Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_THREADS / 2),
              exit_on_error=false, revise=false, propagate_project=true, [seed], [julia_args::Cmd])

运行以下列出的Julia单元测试 测试,可以是字符串或字符串数组,使用 n.核数机 处理器。 如果 exit_on_error错误,当一个测试失败时,其他文件中的所有剩余测试仍将运行;否则将丢弃它们,当 exit_on_error==真. 如果 修订本真的,的 修订本 包用于加载任何修改 基地 或者在运行测试之前到标准库。 如果 传播_项目 当前项目传播到测试环境是真的。 如果种子是通过关键字参数提供的,则它用于在运行测试的上下文中为全局RNG提供种子;否则种子是随机选择的。 论点 朱莉亚*阿格斯 可用于传递自定义 朱莉娅 测试进程的命令行标志。

基本单元测试

测试 模块提供简单的_unit testing_功能。 单元测试是一种通过检查结果是否符合您的期望来查看代码是否正确的方法。 确保代码在进行更改后仍然有效会很有帮助,并且可以在开发时用作指定代码完成后应具有的行为的一种方式。 您可能还想查看以下文档https://pkgdocs.julialang.org/dev/creating-packages/#Adding-tests-to-the-package[将测试添加到您的Julia包]。

简单的单元测试可以用 @测试@test_throws 宏:

@test ex
@test f(args...) key=val ...
@test ex broken=true
@test ex skip=true

测试表达式 评估至 真的. 如果在一个 @测试集,返回一个 通行证 结果 如果是的话,一个 失败 结果 如果是 错误,以及一个 错误 结果 如果无法评估。 如果在a之外执行 @测试集,抛出异常而不是返回 失败错误.

*例子*

julia> @test true
Test Passed

julia> @test [1, 2] + [2, 1] == [3, 3]
Test Passed

@测试f(args。..)键=val。.. 形式相当于写作 @测试f(args。..,键=val。..) 当表达式是使用中缀语法(如近似比较)的调用时,这可能很有用:

julia> @test π ≈ 3.14 atol=0.01
Test Passed

这相当于丑陋的测试 @test∞(π,3.14,atol=0.01). 提供多个表达式是错误的,除非第一个是调用表达式,其余的是赋值(k=v).

您可以使用任何键的 键=val 争论,除了 破碎了跳过,在 @测试:

* 破碎=cond 指示应通过但当前始终失败的测试。 cond==真. 测试表达式 评估至 错误 或导致异常。 返回a 破碎了 结果 如果是的话,或者 错误 结果 如果表达式计算为 真的. 定期 @测试ex 时进行评估 cond==错误. * 跳过=cond 将不应执行但应包含在测试摘要报告中的测试标记为 破碎了,当 cond==真. 这对于间歇性失败的测试或尚未实现功能的测试非常有用。 定期 @测试ex 时进行评估 cond==错误.

*例子*

julia> @test 2 + 2 ≈ 6 atol=1 broken=true
Test Broken
  Expression: ≈(2 + 2, 6, atol = 1)

julia> @test 2 + 2 ≈ 5 atol=1 broken=false
Test Passed

julia> @test 2 + 2 == 5 skip=true
Test Broken
  Skipped: 2 + 2 == 5

julia> @test 2 + 2 == 4 skip=false
Test Passed
兼容性

朱莉娅1.7 破碎了跳过 关键字参数至少需要Julia1.7。

@test_throws exception expr

测试表达式 expr 投掷 例外情况. 异常可以指定显示的错误消息中出现的类型、字符串、正则表达式或字符串列表、匹配函数或值(将通过比较字段来测试是否相等)。 请注意 @test_throws 不支持尾随关键字表单。

兼容性

Julia1.8能够将类型或值以外的任何内容指定为 例外情况 需要Julia v1.8或更高版本。

*例子*

julia> @test_throws BoundsError [1, 2, 3][4]
Test Passed
      Thrown: BoundsError

julia> @test_throws DimensionMismatch [1, 2, 3] + [1, 2]
Test Passed
      Thrown: DimensionMismatch

julia> @test_throws "Try sqrt(Complex" sqrt(-1)
Test Passed
     Message: "DomainError with -1.0:\nsqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x))."

在最后一个例子中,不是匹配单个字符串,而是可以使用:

* ["尝试","复杂"] (字符串列表) * r"尝试sqrt\([Cc]omplex" (正则表达式) * str->发生素("复杂",str) (匹配函数)

例如,假设我们要检查我们的新函数 食物(x) 按预期工作:

julia> using Test

julia> foo(x) = length(x)^2
foo (generic function with 1 method)

如果条件为真,则a 通行证 被退回:

julia> @test foo("bar") == 9
Test Passed

julia> @test foo("fizz") >= 10
Test Passed

如果条件为假,则a 失败 返回并抛出异常:

julia> @test foo("f") == 20
Test Failed at none:1
  Expression: foo("f") == 20
   Evaluated: 1 == 20

ERROR: There was an error during testing

如果由于引发了异常而无法评估条件,则在这种情况下发生的原因是 长度 没有为符号定义, 错误 返回对象并抛出异常:

julia> @test foo(:cat) == 1
Error During Test
  Test threw an exception of type MethodError
  Expression: foo(:cat) == 1
  MethodError: no method matching length(::Symbol)
  The function `length` exists, but no method is defined for this combination of argument types.

  Closest candidates are:
    length(::SimpleVector) at essentials.jl:256
    length(::Base.MethodList) at reflection.jl:521
    length(::MethodTable) at reflection.jl:597
    ...
  Stacktrace:
  [...]
ERROR: There was an error during testing

如果我们期望计算表达式_should_抛出异常,那么我们可以使用 @test_throws 检查是否发生这种情况:

julia> @test_throws MethodError foo(:cat)
Test Passed
      Thrown: MethodError

使用测试集

通常会使用大量测试来确保函数在一系列输入中正常工作。 如果测试失败,默认行为是立即抛出异常。 但是,通常最好先运行其余的测试,以便更好地了解正在测试的代码中有多少错误。

注意 @测试集 在运行测试时,将创建一个自己的本地范围。

@测试集 宏可用于将测试分组为_sets_。 将运行测试集中的所有测试,并在测试集中的末尾打印摘要。 如果任何测试失败,或者由于错误而无法评估,测试集将抛出 TestSetException异常.

@testset [CustomTestSet] [options...] ["description"] begin test_ex end
@testset [CustomTestSet] [options...] ["description $v"] for v in itr test_ex end
@testset [CustomTestSet] [options...] ["description $v, $w"] for v in itrv, w in itrw test_ex end
@testset [CustomTestSet] [options...] ["description"] test_func()
@testset let v = v, w = w; test_ex; end

*使用begin/end或函数调用*

当使用@testset时,使用begin/end或单个函数调用,宏将启动一个新的测试集,在其中计算给定的表达式。

如果没有给出自定义测试集类型,则默认为创建 默认值;默认值. 默认值;默认值 记录所有结果,如果有 失败s或 错误s,在顶层(非嵌套)测试集的末尾抛出异常,以及测试结果的摘要。

任何自定义测试集类型( [医]抽象集)可以给出,它也将用于任何嵌套 @测试集 调用。 给定的选项仅应用于给定它们的测试集。 默认测试集类型接受以下选项:

* 详细::Bool:如果 真的,即使嵌套测试集全部通过,也会显示结果摘要(默认为 错误). * 表演时间:布尔:如果 真的,显示每个显示的测试集的持续时间(默认为 真的). * 失败::Bool:如果 真的,任何测试失败或错误都会导致测试集和任何子测试集立即返回(默认为 错误). 这也可以通过env var全局设置 朱莉亚*泰斯特*快. * Rng::随机。[医]抽象:使用给定的随机数生成器(RNG)作为测试集的全局生成器。 [医]rng 必须是 收到!-能。 此选项可用于本地重现仅依赖于全局RNG状态的随机测试失败。

兼容性

朱莉娅1.8 @testset test_func() 至少需要Julia1.8。

兼容性

朱莉娅1.9 失败,失败 至少需要Julia1.9。

兼容性

朱莉娅1.12 [医]rng option至少需要Julia1.12。

描述字符串接受来自循环索引的插值。 如果没有提供描述,则根据变量构造一个。 如果提供了函数调用,则将使用其名称。 显式描述字符串会复盖此行为。

默认情况下 @测试集 宏将返回testset对象本身,尽管此行为可以在其他testset类型中自定义。 如果一个 使用循环,然后宏收集并返回 完成 方法,默认情况下将返回每次迭代中使用的testset对象的列表。

在执行a的身体之前 @测试集,有一个隐式调用 收到!(随机。default_rng(),rng) 哪里 [医]rng 是当前任务的RNG,还是通过 [医]rng 选择。 此外,在执行主体之后,全局RNG的状态恢复到它之前的状态。 @测试集. 这是为了在发生故障时简化可重复性,并允许无缝重新安排 @测试集s不管它们对全局RNG状态的副作用如何。

嵌套测试集的RNG,除非与 [医]rng 选项,在所有嵌套测试集的开头设置相同的RNG。 当测试集出现故障时打印到屏幕的RNG是最外层测试集的全局RNG,即使内部测试集具有用户手动设置的不同Rng。

*例子*

julia> @testset "trigonometric identities" begin
           θ = 2/3*π
           @test sin(-θ) ≈ -sin(θ)
           @test cos(-θ) ≈ cos(θ)
           @test sin(2θ) ≈ 2*sin(θ)*cos(θ)
           @test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
       end;
Test Summary:            | Pass  Total  Time
trigonometric identities |    4      4  0.2s

*`@测试集`*

何时 @测试集 被使用时,宏为所提供的循环的每次迭代启动新的测试。 在其他方面,每个测试集的语义都与该测试集的语义相同 开始/结束 情况(好像用于每个循环迭代)。

*`@testset让`*

何时 @testset让 使用时,宏启动_transparent_测试集,将给定对象作为上下文对象添加到其中包含的任何失败测试中。 当对一个较大的对象执行一组相关测试时,这很有用,当任何单个测试失败时,最好打印此较大的对象。 透明测试集不会在测试集层次结构中引入额外的嵌套级别,而是直接传递到父测试集(上下文对象附加到任何失败的测试。)

兼容性

朱莉娅1.9 @testset让 至少需要Julia1.9。

兼容性

朱莉娅1.10多 自Julia1.10开始支持分配。

*特殊隐含的世界年龄增量 @测试集开始*

内心的世界时代 @测试集开始 在每个语句之后隐式递增。 这与普通顶级代码的行为相匹配,但与普通代码的行为不匹配 开始/结束 块,即相对于世界年龄, @测试集开始 行为就像身体一样。 开始/结束 块是在顶层写的。

*例子*

julia> @testset let logi = log(im)
           @test imag(logi) == π/2
           @test !iszero(real(logi))
       end
Test Failed at none:3
  Expression: !(iszero(real(logi)))
     Context: logi = 0.0 + 1.5707963267948966im

ERROR: There was an error during testing

julia> @testset let logi = log(im), op = !iszero
           @test imag(logi) == π/2
           @test op(real(logi))
       end
Test Failed at none:3
  Expression: op(real(logi))
     Context: logi = 0.0 + 1.5707963267948966im
              op = !iszero

ERROR: There was an error during testing
TestSetException

当测试集完成且并非所有测试都通过时抛出。

我们可以测试 食物(x) 测试集中的功能:

julia> @testset "Foo Tests" begin
           @test foo("a")   == 1
           @test foo("ab")  == 4
           @test foo("abc") == 9
       end;
Test Summary: | Pass  Total  Time
Foo Tests     |    3      3  0.0s

测试集也可以嵌套:

julia> @testset "Foo Tests" begin
           @testset "Animals" begin
               @test foo("cat") == 9
               @test foo("dog") == foo("cat")
           end
           @testset "Arrays $i" for i in 1:3
               @test foo(zeros(i)) == i^2
               @test foo(fill(1.0, i)) == i^2
           end
       end;
Test Summary: | Pass  Total  Time
Foo Tests     |    8      8  0.0s

以及调用函数:

julia> f(x) = @test isone(x)
f (generic function with 1 method)

julia> @testset f(1);
Test Summary: | Pass  Total  Time
f             |    1      1  0.0s

这可用于允许对测试集进行因式分解,从而通过运行关联函数来更轻松地运行单个测试集。 请注意,在函数的情况下,测试集将被赋予被调用函数的名称。 如果嵌套测试集没有失败,就像这里发生的那样,它将隐藏在摘要中,除非 详细=真 选项通过:

julia> @testset verbose = true "Foo Tests" begin
           @testset "Animals" begin
               @test foo("cat") == 9
               @test foo("dog") == foo("cat")
           end
           @testset "Arrays $i" for i in 1:3
               @test foo(zeros(i)) == i^2
               @test foo(fill(1.0, i)) == i^2
           end
       end;
Test Summary: | Pass  Total  Time
Foo Tests     |    8      8  0.0s
  Animals     |    2      2  0.0s
  Arrays 1    |    2      2  0.0s
  Arrays 2    |    2      2  0.0s
  Arrays 3    |    2      2  0.0s

如果我们确实有测试失败,则只会显示失败测试集的详细信息:

julia> @testset "Foo Tests" begin
           @testset "Animals" begin
               @testset "Felines" begin
                   @test foo("cat") == 9
               end
               @testset "Canines" begin
                   @test foo("dog") == 9
               end
           end
           @testset "Arrays" begin
               @test foo(zeros(2)) == 4
               @test foo(fill(1.0, 4)) == 15
           end
       end

Arrays: Test Failed
  Expression: foo(fill(1.0, 4)) == 15
   Evaluated: 16 == 15
[...]
Test Summary: | Pass  Fail  Total  Time
Foo Tests     |    3     1      4  0.0s
  Animals     |    2            2  0.0s
  Arrays      |    1     1      2  0.0s
ERROR: Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken.

测试日志语句

人们可以使用 @test_logs宏测试日志语句,或使用 测试日志.

@test_logs [log_patterns...] [keywords] expression

收集由 表达方式 使用 collect_test_logs,检查它们是否匹配序列 log_patterns,并返回值 表达方式. 该 关键词 提供一些简单的日志记录过滤: 最小级别 关键字控制将为测试收集的最小日志级别, match_mode 关键字定义如何执行匹配(默认值 :全部 检查所有日志和模式是否两两匹配;使用 :任何 检查模式在序列的某个地方至少匹配一次。)

最有用的日志模式是形式的简单元组 (级别,消息). 可以使用不同数量的元组元素来匹配其他日志元数据,对应于要传递给的参数 [医]抽象日志 通过 处理/处理 功能: (级别,消息,模块,组,id,文件,行). 存在的元素将与日志记录字段两两匹配,使用 == 默认情况下,在特殊情况下, 符号s可用于标准日志级别,以及 正则表达式模式中的s将匹配字符串或符号字段,使用 发生,发生.

*例子*

考虑一个记录警告和几个调试消息的函数:

function foo(n)
    @info "Doing foo with n=$n"
    for i=1:n
        @debug "Iteration $i"
    end
    42
end

我们可以使用

@test_logs (:info,"Doing foo with n=2") foo(2)

如果我们还想测试调试消息,则需要使用 最小级别 关键字:

using Logging
@test_logs (:info,"Doing foo with n=2") (:debug,"Iteration 1") (:debug,"Iteration 2") min_level=Logging.Debug foo(2)

如果您想测试某些特定消息是生成的,而忽略其余消息,则可以设置关键字 match_mode=:任何:

using Logging
@test_logs (:info,) (:debug,"Iteration 42") min_level=Logging.Debug match_mode=:any foo(100)

宏可以链接到 @测试 还要测试返回的值:

@test (@test_logs (:info,"Doing foo with n=2") foo(2)) == 42

如果要测试是否没有警告,可以省略指定日志模式并设置 最小级别 因此:

# test that the expression logs no messages when the logger level is warn:
@test_logs min_level=Logging.Warn @info("Some information") # passes
@test_logs min_level=Logging.Warn @warn("Some information") # fails

如果你想测试没有警告(或错误消息)在 斯德尔 不是由 @警告,见 xref:stdlib/Test.adoc#Test.@test_nowarn[@test_nowarn.

TestLogger(; min_level=Info, catch_exceptions=false)

创建一个 测试日志 它在其捕获记录的消息 日志::矢量{LogRecord} 场。

套装 最小级别 控制 日志级别, catch_exceptions 是否应捕获作为日志事件生成的一部分抛出的异常,以及 尊敬的人 是否遵循记录消息的约定 maxlog=n 对于一些整数 n 最多 n 时代。

请参阅: 日志记录.

*例子*

julia> using Test, Logging

julia> f() = @info "Hi" number=5;

julia> test_logger = TestLogger();

julia> with_logger(test_logger) do
           f()
           @info "Bye!"
       end

julia> @test test_logger.logs[1].message == "Hi"
Test Passed

julia> @test test_logger.logs[1].kwargs[:number] == 5
Test Passed

julia> @test test_logger.logs[2].message == "Bye!"
Test Passed
LogRecord

存储单个日志事件的结果。 字段:

* 水平日志级别的日志消息 * 信息:日志消息的文本内容 * [医]模式:日志事件的模块 * 团体:日志记录组(默认情况下,包含日志事件的文件的名称) * 身份证:日志事件的ID * 档案:包含日志事件的文件 * :日志事件文件内的行 * 夸格斯:传递给日志事件的任何关键字参数

其他测试宏

由于浮点值的计算可能不精确,您可以使用以下任一项执行近似相等性检查 @测试a≈b (哪里 ,通过选项卡完成键入 \约,是 [医伊萨普罗克斯]功能)或使用 [医伊萨普罗克斯]直接。

julia> @test 1 ≈ 0.999999999
Test Passed

julia> @test 1 ≈ 0.999999
Test Failed at none:1
  Expression: 1 ≈ 0.999999
   Evaluated: 1 ≈ 0.999999

ERROR: There was an error during testing

您可以通过设置指定相对和绝对公差 rtolatol公司 关键字参数 [医]伊萨普罗克斯,分别在 比较:

julia> @test 1 ≈ 0.999999  rtol=1e-5
Test Passed

请注意,这不是 而是一个普遍的特点 @测试 宏: @测试a<op>b键=val 被宏转换成 @测试op(a,b,key=val). 然而,它对…​…​特别有用。 测试。

@inferred [AllowedType] f(x)

测试调用表达式 f(x) 返回编译器推断的相同类型的值。 检查类型稳定性很有用。 f(x) 可以是任何调用表达式。 返回 f(x) 如果类型匹配,并且 错误 结果 如果它发现不同的类型。

可选择, 允许类型 放松测试,使其通过时,无论是类型 f(x) 匹配推断的类型模 允许类型,或者当返回类型是 允许类型. 这在测试返回小联合的函数的类型稳定性时很有用,例如 工会{Nothing, T}工会{Missing, T}.

julia> f(a) = a > 1 ? 1 : 1.0
f (generic function with 1 method)

julia> typeof(f(2))
Int64

julia> @code_warntype f(2)
MethodInstance for f(::Int64)
  from f(a) @ Main none:1
Arguments
  #self#::Core.Const(f)
  a::Int64
Body::UNION{FLOAT64, INT64}
1 ─ %1 = :>::Core.Const(>)
│   %2 = (%1)(a, 1)::Bool
└──      goto #3 if not %2
2 ─      return 1
3 ─      return 1.0

julia> @inferred f(2)
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
[...]

julia> @inferred max(1, 2)
2

julia> g(a) = a < 10 ? missing : 1.0
g (generic function with 1 method)

julia> @inferred g(20)
ERROR: return type Float64 does not match inferred return type Union{Missing, Float64}
[...]

julia> @inferred Missing g(20)
1.0

julia> h(a) = a < 10 ? missing : f(a)
h (generic function with 1 method)

julia> @inferred Missing h(20)
ERROR: return type Int64 does not match inferred return type Union{Missing, Float64, Int64}
[...]
@test_deprecated [pattern] expression

何时 --depwarn=是,测试一下 表达方式 发出弃用警告并返回 表达方式. 日志消息字符串将与之匹配 模式 默认为 r"弃用"i.

何时 --depwarn=不,简单的返回执行的结果 表达方式. 何时 --depwarn=错误,检查是否抛出ErrorException。

*例子*

# Deprecated in julia 0.7
@test_deprecated num2hex(1)

# The returned value can be tested by chaining with @test:
@test (@test_deprecated num2hex(1)) == "0000000000000001"
@test_warn msg expr

测试是否评估 expr 结果 斯德尔包含 味精 字符串或匹配 味精 正则表达式。 如果 味精 是一个布尔函数,测试是否 味精(输出) 申报表 真的. 如果 味精 是一个元组或数组,检查错误输出是否包含/匹配 味精. 返回评估的结果 expr.

请参阅 @test_nowarn检查是否没有错误输出。

注:由 @警告 无法使用此宏进行测试。 使用方法 @test_logs代替。

@test_nowarn expr

测试是否评估 expr 结果为空 斯德尔输出(没有警告或其他消息)。 返回计算结果 expr.

注意:没有由 @警告 无法使用此宏进行测试。 使用方法 @test_logs代替。

破碎的测试

如果测试始终失败,可以更改为使用 @test_broken 宏。 这将表示测试为 破碎了 如果测试继续失败,并通过 错误 如果测试成功。

@test_broken ex
@test_broken f(args...) key=val ...

指示应通过但当前始终失败的测试。 测试表达式 评估至 错误 或导致异常。 返回a 破碎了 结果 如果是的话,或者 错误 结果 如果表达式计算为 真的. 这相当于 @测试ex破=真.

@test_broken f(args。..)键=val。.. 表格适用于 @测试 宏。

*例子*

julia> @test_broken 1 == 2
Test Broken
  Expression: 1 == 2

julia> @test_broken 1 == 2 atol=0.1
Test Broken
  Expression: ==(1, 2, atol = 0.1)

@test_skip 也可在没有评估的情况下跳过测试,但将跳过的测试计入测试集报告中。 测试不会运行,但给出了一个 破碎了 结果.

@test_skip ex
@test_skip f(args...) key=val ...

将不应执行但应包含在测试摘要报告中的测试标记为 破碎了. 这对于间歇性失败的测试或尚未实现的功能的测试非常有用。 这相当于 @测试ex跳过=真.

@test_skip f(args。..)键=val。.. 表格适用于 @测试 宏。

*例子*

julia> @test_skip 1 == 2
Test Broken
  Skipped: 1 == 2

julia> @test_skip 1 == 2 atol=0.1
Test Broken
  Skipped: ==(1, 2, atol = 0.1)

测试结果类型

Test.Result

所有测试都会生成一个结果对象。 此对象可以存储也可以不存储,具体取决于测试是否是测试集的一部分。

Test.Pass <: Test.Result

测试条件为true,即表达式求值为true或抛出正确的异常。

Test.Fail <: Test.Result

测试条件为false,即表达式求值为false或未抛出正确的异常。

Test.Error <: Test.Result

由于异常而无法评估测试条件,或者它评估为a以外的其他内容 布尔. 的情况下 @test_broken 它用于表示意外 通行证 结果 发生了。

Test.Broken <: Test.Result

测试条件是中断测试的预期(失败)结果,或者是明确跳过的 @test_skip.

创建自定义 [医]抽象集 类别

包可以创建自己的 [医]抽象集 通过实现子类型 记录完成 方法。 子类型应该有一个接受描述字符串的单参数构造函数,并将任何选项作为关键字参数传入。

record(ts::AbstractTestSet, res::Result)

将结果记录到测试集。 此函数由 @测试集 基础设施每次包含 @测试 宏完成,并给出测试结果(可能是 错误). 这也将与一个调用 错误 如果在测试块内部但在测试块外部抛出异常 @测试 上下文。

finish(ts::AbstractTestSet)

执行给定测试集所需的任何最终处理。 这是由 @测试集 测试块执行后的基础结构。

海关规定 [医]抽象集 子类型应调用 记录 在他们的父母(如果有的话)将自己添加到测试结果树中。 这可以实现为:

if get_testset_depth() != 0
    # Attach this test set to the parent test set
    parent_ts = get_testset()
    record(parent_ts, self)
    return self
end

测试 负责在执行嵌套测试集时维护堆栈,但任何结果累积都是 [医]抽象集 子类型。 您可以使用 get_testsetget_testset_depth 方法。 请注意,这些函数不会导出。

get_testset()

从任务的本地存储中检索活动测试集。 如果没有测试集处于活动状态,请使用回退默认测试集。

get_testset_depth()

返回活动测试集的数量,不包括默认测试集

测试 还要确保嵌套 @测试集 调用使用相同的 [医]抽象集 子类型作为其父级,除非它被显式设置。 它不会传播测试集的任何属性。 选项继承行为可以通过使用堆栈基础结构的包来实现 测试 提供。

定义基本 [医]抽象集 子类型可能看起来像:

import Test: Test, record, finish
using Test: AbstractTestSet, Result, Pass, Fail, Error
using Test: get_testset_depth, get_testset
struct CustomTestSet <: Test.AbstractTestSet
    description::AbstractString
    foo::Int
    results::Vector
    # constructor takes a description string and options keyword arguments
    CustomTestSet(desc; foo=1) = new(desc, foo, [])
end

record(ts::CustomTestSet, child::AbstractTestSet) = push!(ts.results, child)
record(ts::CustomTestSet, res::Result) = push!(ts.results, res)
function finish(ts::CustomTestSet)
    # just record if we're not the top-level parent
    if get_testset_depth() > 0
        record(get_testset(), ts)
        return ts
    end

    # so the results are printed if we are at the top level
    Test.print_test_results(ts)
    return ts
end

使用该测试集看起来像:

@testset CustomTestSet foo=4 "custom testset inner 2" begin
    # this testset should inherit the type, but not the argument.
    @testset "custom testset inner" begin
        @test true
    end
end

为了使用自定义测试集并将记录的结果打印为任何外部默认测试集的一部分,还要定义 测试。get_test_帐户. 这可能看起来像这样:

using Test: AbstractTestSet, Pass, Fail, Error, Broken, get_test_counts, TestCounts, format_duration

function Test.get_test_counts(ts::CustomTestSet)
    passes, fails, errors, broken = 0, 0, 0, 0
    # cumulative results
    c_passes, c_fails, c_errors, c_broken = 0, 0, 0, 0

    for t in ts.results
        # count up results
        isa(t, Pass)   && (passes += 1)
        isa(t, Fail)   && (fails  += 1)
        isa(t, Error)  && (errors += 1)
        isa(t, Broken) && (broken += 1)
        # handle children
        if isa(t, AbstractTestSet)
            tc = get_test_counts(t)::TestCounts
            c_passes += tc.passes + tc.cumulative_passes
            c_fails  += tc.fails + tc.cumulative_fails
            c_errors += tc.errors + tc.cumulative_errors
            c_broken += tc.broken + tc.cumulative_broken
        end
    end
    # get a duration, if we have one
    duration = format_duration(ts)
    return TestCounts(true, passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration)
end
TestCounts

保存用于递归收集测试集结果以用于显示目的的状态。

字段:

* 定制:是否功能 get_test_帐户 是为 [医]抽象集 这个计数对象是为了. 如果定义了自定义方法,则始终通过 真的 到构造函数。 * 通行证:通过次数 @测试 调用。 * 失败:失败的次数 @测试 调用。 * 错误:错误的数量 @测试 调用。 * 破碎了:破碎的数量 @测试 调用。 * 通行证:累计通过次数 @测试 调用。 * 失败:累计失败次数 @测试 调用。 * 错误:累计出错次数 @测试 调用。 * 破碎了:累计破碎次数 @测试 调用。 * 持续时间:总持续时间 [医]抽象集 有问题的跑了,作为一个格式化 字符串.

"get_test_counts(::AbstractTestSet)->TestCounts

递归函数,直接计算测试集中每种类型的测试结果的数量,并计算子测试集的总数。

海关规定 [医]抽象集 应该实现这个函数来计算和显示它们的总数 默认值;默认值 ""好吧。

如果没有为自定义实现这一点 测试集,打印回落到报告 x 对于失败和 ?s 的持续时间。

format_duration(::AbstractTestSet)

返回一个格式化的字符串,用于打印测试集运行的持续时间。

如果未定义,则回退到 "?s".

print_test_results(ts::AbstractTestSet, depth_pad=0)

打印结果 [医]抽象集 作为格式化表。 depth_pad 指在所有输出前面应该加多少填充。

在…​…​ 测试。完成,如果 完成ed测试集是最顶层的测试集。

测试实用程序

通用阵列 可用于测试编程到 抽象阵列 接口,以确保函数可以与标准之外的数组类型一起工作 阵列 类型。

[医]普通型 可用于测试编程到 摘要;摘要 接口,以确保功能可以与标准之外的关联类型一起工作 Dict,Dict 类型。

通用顺序 可用于测试Api对泛型有序类型的支持。

通用集 可用于测试编程到 抽象集,抽象集 接口,以确保功能可以与标准之外的set类型一起工作 套装比特集 类型。

通用字符串 可用于测试编程到 抽象字符串 接口,以确保函数可以与标准之外的字符串类型一起工作 字符串 类型。

detect_ambiguities(mod1, mod2...; recursive=false,
                                  ambiguous_bottom=false,
                                  allowed_undefineds=nothing)

返回一个向量 (方法,方法) 指定模块中定义的多义方法对。 使用方法 递归=真 以在所有子模块中进行测试。

模棱两可的,模棱两可的 控制是否仅由以下因素触发歧义 联合{} 类型参数包括在内;在大多数情况下,您可能希望将其设置为 错误. 见 基地。鹿isambiguous.

测试。检测_unbound_args对于一个解释 允许定义.

兼容性

朱莉娅1.8 允许定义 至少需要Julia1.8。

detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)

返回一个向量 方法s可能具有未绑定的类型参数。 使用方法 递归=真 以在所有子模块中进行测试。

默认情况下,任何未定义的符号都会触发警告。 此警告可以通过提供以下集合来抑制 [医]GlobalRef可以跳过警告的s。 例如,设置

allowed_undefineds = Set([GlobalRef(Base, :active_repl),
                          GlobalRef(Base, :active_repl_backend)])

会抑制关于 基地。活动_repl基地。活动_repl_backend.

兼容性

朱莉娅1.8 允许定义 至少需要Julia1.8。

测试包的工作流程

使用前面部分中提供给我们的工具,以下是创建包并向其添加测试的潜在工作流程。

生成示例包

对于此工作流,我们将创建一个名为 例子::

pkg> generate Example
shell> cd Example
shell> mkdir test
pkg> activate .

创建示例函数

测试包的首要要求是具有要测试的功能。 为此,我们将添加一些简单的函数 例子: 我们可以测试。 将以下内容添加到 src/示例。jl:

module Example

export greet, simple_add, type_multiply

function greet()
    "Hello world!"
end

function simple_add(a, b)
    a + b
end

function type_multiply(a::Float64, b::Float64)
    a &ast; b
end

end

创建测试环境

从根部 例子: 包,导航到 测试 目录,在那里激活一个新的环境,并添加 测试 包到环境:

shell> cd test
pkg> activate .
(test) pkg> add Test

测试我们的包装

现在,我们准备将测试添加到 例子:. 在 测试 目录调用 符咒。jl 其中包含我们要运行的测试集。 继续并在 测试 目录并在其中添加以下代码:

using Example
using Test

@testset "Example tests" begin

    @testset "Math tests" begin
        include("math_tests.jl")
    end

    @testset "Greeting tests" begin
        include("greeting_tests.jl")
    end
end

我们需要创建这两个包含的文件, math_tests.jlgreeting_tests.jl,并为它们添加一些测试。

注意:注意我们如何不必指定添加 例子: 进入 测试 环境的 工程。汤姆尔. 这是朱莉娅的测试系统的一个好处,你可以https://pkgdocs.julialang.org/dev/creating-packages/[在这里阅读更多]。

编写测试 math_tests.jl

利用我们的知识 测试。jl,以下是我们可以添加到的一些示例测试 math_tests.jl:

@testset "Testset 1" begin
    @test 2 == simple_add(1, 1)
    @test 3.5 == simple_add(1, 2.5)
        @test_throws MethodError simple_add(1, "A")
        @test_throws MethodError simple_add(1, 2, 3)
end

@testset "Testset 2" begin
    @test 1.0 == type_multiply(1.0, 1.0)
        @test isa(type_multiply(2.0, 2.0), Float64)
    @test_throws MethodError type_multiply(1, 2.5)
end

编写测试 greeting_tests.jl

利用我们的知识 测试。jl,以下是我们可以添加到的一些示例测试 greeting_tests.jl:

@testset "Testset 3" begin
    @test "Hello world!" == greet()
    @test_throws MethodError greet("Antonia")
end

测试我们的包装

现在我们已经添加了我们的测试和我们的 符咒。jl 脚本在 测试,我们可以测试我们的 例子: 通过返回到 例子: 包环境并重新激活 例子: 环境问题:

shell> cd ..
pkg> activate .

从那里,我们终于可以运行我们的测试套件,如下所示:

(Example) pkg> test
     Testing Example
      Status `/tmp/jl_Yngpvy/Project.toml`
  [fa318bd2] Example v0.1.0 `/home/src/Projects/tmp/errata/Example`
  [8dfed614] Test `@stdlib/Test`
      Status `/tmp/jl_Yngpvy/Manifest.toml`
  [fa318bd2] Example v0.1.0 `/home/src/Projects/tmp/errata/Example`
  [2a0f44e3] Base64 `@stdlib/Base64`
  [b77e0a4c] InteractiveUtils `@stdlib/InteractiveUtils`
  [56ddb016] Logging `@stdlib/Logging`
  [d6f4376e] Markdown `@stdlib/Markdown`
  [9a3f8284] Random `@stdlib/Random`
  [ea8e919c] SHA `@stdlib/SHA`
  [9e88b42a] Serialization `@stdlib/Serialization`
  [8dfed614] Test `@stdlib/Test`
     Testing Running tests...
Test Summary: | Pass  Total
Example tests |    9      9
     Testing Example tests passed

如果一切顺利,你应该看到类似的输出如上所述。 使用 测试。jl,可以为包添加更复杂的测试,但理想情况下,这应该为开发人员指明如何开始测试他们自己创建的包的方向。

代码复盖率

测试期间的代码复盖率跟踪可以使用 pkg>测试—​复盖范围 标志(或在较低级别使用 --代码-复盖范围朱莉娅arg)。 默认情况下,这是在https://github.com/julia-actions/julia-runtest[julia-runtest]GitHub操作。

要评估复盖范围,请手动检查 .cov 在本地或CI中在源文件旁边生成的文件使用https://github.com/julia-actions/julia-processcoverage[julia-processcoverage]GitHub操作。

兼容性

Julia1.11自Julia1.11以来,在包预编译阶段不会收集coverage。