Поиск…


Синтаксис

  • @test [expr]
  • @test_throws [Исключение] [expr]
  • @testset "[name]" begin; [Тесты]; конец
  • Pkg.test ([пакет])

замечания

Стандартная библиотечная документация для Base.Test охватывает дополнительные материалы, кроме тех, что показаны в этих примерах.

Тестирование пакета

Для запуска модульных тестов для пакета используйте функцию Pkg.test . Для пакета с именем MyPackage команда будет

julia> Pkg.test("MyPackage")

Ожидаемый результат будет аналогичен

INFO: Computing test dependencies for MyPackage...
INFO: Installing BaseTestNext v0.2.2
INFO: Testing MyPackage
Test Summary: | Pass  Total
  Data        |   66     66
Test Summary: | Pass  Total
  Monetary    |  107    107
Test Summary: | Pass  Total
  Basket      |   47     47
Test Summary: | Pass  Total
  Mixed       |   13     13
Test Summary: | Pass  Total
  Data Access |   35     35
INFO: MyPackage tests passed
INFO: Removing BaseTestNext v0.2.2

хотя, очевидно, нельзя ожидать, что он будет точно соответствовать указанному выше, поскольку разные пакеты используют разные фреймворки.

Эта команда запускает файл test/runtests.jl в чистой среде.

Можно проверить все установленные пакеты одновременно с помощью

julia> Pkg.test()

но это обычно занимает очень много времени.

Написание простого теста

test/runtests.jl тесты объявляются в файле test/runtests.jl в пакете. Как правило, этот файл начинается

using MyModule
using Base.Test

Базовой единицей тестирования является макрос @test . Этот макрос подобен утверждению. Любое булевское выражение может быть проверено в макросе @test :

@test 1 + 1 == 2
@test iseven(10)
@test 9 < 10 || 10 < 9

Мы можем попробовать макрос @test в REPL:

julia> using Base.Test

julia> @test 1 + 1 == 2
Test Passed
  Expression: 1 + 1 == 2
   Evaluated: 2 == 2

julia> @test 1 + 1 == 3
Test Failed
  Expression: 1 + 1 == 3
   Evaluated: 2 == 3
ERROR: There was an error during testing
 in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
 in do_test(::Base.Test.Returned, ::Expr) at ./test.jl:281

Тест-макрос можно использовать практически в любом месте, например, в циклах или функциях:

# For positive integers, a number's square is at least as large as the number
for i in 1:10
    @test i^2 ≥ i
end

# Test that no two of a, b, or c share a prime factor
function check_pairwise_coprime(a, b, c)
    @test gcd(a, b) == 1
    @test gcd(a, c) == 1
    @test gcd(b, c) == 1
end

check_pairwise_coprime(10, 23, 119)

Написание тестового набора

0.5.0

В версии v0.5 тестовые наборы встроены в стандартный модуль Base.Test библиотеки, и вам не нужно ничего делать (кроме using Base.Test ), чтобы использовать их.

0.4.0

Тестовые наборы не являются частью библиотеки Base.Test Julia 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 s вместе в тестовом наборе. В дополнение к более четкой организации тестирования тестовые комплекты обеспечивают лучшую производительность и большую настраиваемость.

Чтобы определить тестовый набор, просто оберните любое количество @test s блоком @testset :

@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 с экземпляром singleton, который содержит все. Прежде чем мы даже реализуем этот тип, мы можем использовать основанные на тестах принципы разработки и выполнить тесты:

@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 return true для любого набора:

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

И теперь все наши тесты проходят!

Исключения для тестирования

Исключения, возникающие при запуске теста, не пройдут проверку, и если тест не находится в тестовом наборе, завершите работу механизма тестирования. Обычно это хорошо, потому что в большинстве ситуаций исключения не являются желаемым результатом. Но иногда хочется конкретно проверить, что возникает определенное исключение. Макрос @test_throws облегчает это.

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

Если @test_throws неправильное исключение, @test_throws все равно не будет выполнено:

julia> @test_throws TypeError [1, 2, 3][4]
Test Failed
  Expression: ([1,2,3])[4]
    Expected: TypeError
      Thrown: BoundsError
ERROR: There was an error during testing
 in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
 in do_test_throws(::Base.Test.Threw, ::Expr, ::Type{T}) at ./test.jl:329

и если исключение не будет @test_throws , @test_throws также не будет выполнено:

julia> @test_throws BoundsError [1, 2, 3, 4][4]
Test Failed
  Expression: ([1,2,3,4])[4]
    Expected: BoundsError
  No exception thrown
ERROR: There was an error during testing
 in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
 in do_test_throws(::Base.Test.Returned, ::Expr, ::Type{T}) at ./test.jl:329

Приближенное равенство

Какова сделка со следующим?

julia> @test 0.1 + 0.2 == 0.3
Test Failed
  Expression: 0.1 + 0.2 == 0.3
   Evaluated: 0.30000000000000004 == 0.3
ERROR: There was an error during testing
 in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
 in do_test(::Base.Test.Returned, ::Expr) at ./test.jl:281

Ошибка вызвана тем фактом, что ни один из 0.1 , 0.2 и 0.3 не представлен на компьютере как именно эти значения - 1//10 , 2//10 и 3//10 . Вместо этого они аппроксимируются значениями, которые очень близки. Но, как видно из вышеприведенного теста, при добавлении двух приближений результат может быть немного хуже приближения, чем это возможно. Этой теме гораздо больше , чем здесь.

Но нам не повезло! Чтобы проверить, что комбинация округления с числом с плавающей запятой и арифметикой с плавающей точкой является приблизительно правильной, даже если она не является точной, мы можем использовать функцию isapprox (что соответствует оператору ). Поэтому мы можем переписать наш тест как

julia> @test 0.1 + 0.2 ≈ 0.3
Test Passed
  Expression: 0.1 + 0.2 ≈ 0.3
   Evaluated: 0.30000000000000004 isapprox 0.3

Конечно, если наш код был полностью неправильным, тест все равно поймет, что:

julia> @test 0.1 + 0.2 ≈ 0.4
Test Failed
  Expression: 0.1 + 0.2 ≈ 0.4
   Evaluated: 0.30000000000000004 isapprox 0.4
ERROR: There was an error during testing
 in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
 in do_test(::Base.Test.Returned, ::Expr) at ./test.jl:281

Функция isapprox использует эвристику, основанную на размере чисел и точности типа с плавающей запятой, чтобы определить допустимую погрешность. Он не подходит для всех ситуаций, но он работает в большинстве случаев и экономит много усилий, isapprox собственную версию isapprox .



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow