编写测试集

Version >= 0.5.0

在版本 v0.5 中,测试集内置于标准库 Base.Test 模块中,你无需执行任何特殊操作(除了 using Base.Test)以使用它们。

Version = 0.4.0

测试集不是 Julia v0.4 的 Base.Test 库的一部分。相反,你必须 REQUIRE BaseTestNext 模块,并将 using BaseTestNext 添加到你的文件。要支持版本 0.4 和 0.5,你可以使用

if VERSION ≥ v"0.5.0-dev+7720"
    using Base.Test
else
    using BaseTestNext
    const Test = BaseTestNext
end

在测试集中将相关的 @test 组合在一起是有帮助的。除了更清晰的测试组织,测试集还提供更好的输出和更多的可定制性。

要定义测试集,只需使用 @testset 块包装任意数量的 @test

@testset "+" begin
    @test 1 + 1 == 2
    @test 2 + 2 == 4
end

@testset "*" begin
    @test 1 * 1 == 1
    @test 2 * 2 == 4
end

运行这些测试集将打印以下输出:

Test Summary: | Pass  Total
  +           |    2      2

Test Summary: | Pass  Total
  *           |    2      2

即使测试集包含失败的测试,整个测试集也将运行完成,并且将记录并报告失败:

@testset "-" begin
    @test 1 - 1 == 0
    @test 2 - 2 == 1
    @test 3 - () == 3
    @test 4 - 4 == 0
end

运行此测试集会导致

-: Test Failed
  Expression: 2 - 2 == 1
   Evaluated: 0 == 1
 in record(::Base.Test.DefaultTestSet, ::Base.Test.Fail) at ./test.jl:428
    ...
-: Error During Test
  Test threw an exception of type MethodError
  Expression: 3 - () == 3
  MethodError: no method matching -(::Int64, ::Tuple{})
    ...
Test Summary: | Pass  Fail  Error  Total
  -           |    2     1      1      4
ERROR: Some tests did not pass: 2 passed, 1 failed, 1 errored, 0 broken.
    ...

测试集可以嵌套,允许任意深度组织

@testset "Int" begin
    @testset "+" begin
        @test 1 + 1 == 2
        @test 2 + 2 == 4
    end
    @testset "-" begin
        @test 1 - 1 == 0
    end
end

如果测试通过,那么这将只显示最外层测试集的结果:

Test Summary: | Pass  Total
  Int         |    3      3

但是如果测试失败,则会报告深入到确切的测试集并导致失败的测试。

@testset 宏可与 for 循环一起使用,一次创建多个测试集:

@testset for i in 1:5
    @test 2i == i + i
    @test i^2 == i * i
    @test i ÷ i == 1
end

报道

Test Summary: | Pass  Total
  i = 1       |    3      3
Test Summary: | Pass  Total
  i = 2       |    3      3
Test Summary: | Pass  Total
  i = 3       |    3      3
Test Summary: | Pass  Total
  i = 4       |    3      3
Test Summary: | Pass  Total
  i = 5       |    3      3

常见的结构是使外部测试集测试组件或类型。在这些外部测试集中,内部测试集测试行为。例如,假设我们创建了一个类型 UniversalSet,其中包含一个包含所有内容的单例实例。在我们实现类型之前,我们可以使用测试驱动的开发原则并实现测试:

@testset "UniversalSet" begin
    U = UniversalSet.instance
    @testset "egal/equal" begin
        @test U === U
        @test U == U
    end

    @testset "in" begin
        @test 1 in U
        @test "Hello World" in U
        @test Int in U
        @test U in U
    end

    @testset "subset" begin
        @test Set() ⊆ U
        @test Set(["Hello World"]) ⊆ U
        @test Set(1:10) ⊆ U
        @test Set([:a, 2.0, "w", Set()]) ⊆ U
        @test U ⊆ U
    end
end

然后我们可以开始实现我们的功能,直到它通过测试。第一步是定义类型:

immutable UniversalSet <: Base.AbstractSet end

我们现在只有两个测试通过。我们可以实现 in

immutable UniversalSet <: Base.AbstractSet end
Base.in(x, ::UniversalSet) = true

这也使我们的一些子集测试通过。然而,issubset)回退对 UniversalSet 不起作用,因为回退试图迭代元素,这是我们做不到的。我们可以简单地定义一个特殊化,使得 issubset 可以为任何集合返回 true

immutable UniversalSet <: Base.AbstractSet end
Base.in(x, ::UniversalSet) = true
Base.issubset(x::Base.AbstractSet, ::UniversalSet) = true

现在,我们所有的测试都通过了!