Spec-Zone.ru › Julia 0.6

Единые тесты

Тестирование базовой Julia

Julia активно развивается и имеет обширную тестовую suite для проверки функциональности на разных платформах. Если вы компилируете Julia из исходного кода, вы можете запустить этот набор тестов с помощью make test. В случае установки двоичного файла, вы можете запустить тестовую suite с помощью Base.runtests().

Base.runtestsФункция

runtests([tests=["all"] [, numcores=ceil(Int, Sys.CPU_CORES / 2) ]])

Запускает единичные тесты Julia, перечисленные в tests, которые могут быть строкой или массивом строк, используя numcores процессоры. (не экспортируется)

исходный код

Базовые единичные тесты

Модуль Base.Test предоставляет простую функциональность единичных тестов. Единичный тест – это способ проверить корректность вашего кода, проверяя, соответствуют ли результаты вашим ожиданиям. Это может быть полезно для проверки того, что ваш код по-прежнему работает после внесения изменений, а также может использоваться при разработке для указания поведения, которое ваш код должен иметь при завершении.

Простые единичные тесты могут выполняться с помощью макросов @test() и @test_throws():

Base.Test.@testМакрос

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

Проверяет, что выражение ex вычисляется до true. Возвращает Pass Result если это так, Fail Result если это false, и Error Result если оно не могло быть вычислено.

Форма @test f(args...) key=val... эквивалентна написанию @test f(args..., key=val...), что может быть полезно, когда выражение является вызовом с использованием инфиксной синтаксической конструкции, например, приблизительных сравнений:

@test a ≈ b atol=ε

Это эквивалентно более громоздкому тесту @test ≈(a, b, atol=ε). Недопустимо использовать более одного выражения, если первое не является выражением вызова, а остальные — присваиваниями (k=v).

исходный код

Base.Test.@test_throwsМакрос

@test_throws extype ex

Проверяет, что выражение ex вызывает исключение типа extype. Обратите внимание, что @test_throws не поддерживает форму с последующим ключевым словом.

исходный код

Например, предположим, что мы хотим проверить, что наша новая функция foo(x) работает как ожидается:

julia> using Base.Test

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

Если условие истинно, возвращается Pass:

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

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

Если условие ложно, возвращается Fail и генерируется исключение:

julia> @test foo("f") == 20
Test Failed
  Expression: foo("f") == 20
   Evaluated: 1 == 20
ERROR: There was an error during testing

Если условие не может быть вычислено из-за возникшего исключения, что происходит в данном случае, потому что length() не определен для символов, возвращается объект Error и генерируется исключение:

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)
  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

Если ожидается, что вычисление выражения должно вызвать исключение, мы можем использовать @test_throws() для проверки этого:

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

Работа с наборами тестов

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

Макрос @testset() может использоваться для группировки тестов в наборы. Все тесты в наборе будут выполнены, и в конце набора тестов будет напечатан сводный отчет. Если какие-либо тесты завершились неудачей или не могли быть вычислены из-за ошибки, набор тестов сгенерирует TestSetException.

Base.Test.@testsetМакрос

@testset [CustomTestSet] [option=val  ...] ["description"] begin ... end
@testset [CustomTestSet] [option=val  ...] ["description $v"] for v in (...) ... end
@testset [CustomTestSet] [option=val  ...] ["description $v, $w"] for v in (...), w in (...) ... end

Начинает новый набор тестов или несколько наборов тестов, если задан цикл for.

Если тип набора тестов не указан, по умолчанию создается DefaultTestSet. DefaultTestSet регистрирует все результаты и, если есть какие-либо Fail или Error , генерирует исключение в конце верхнего уровня (не вложенного) набора тестов вместе с отчетом о результатах тестов.

Любой пользовательский тип набора тестов (подтип AbstractTestSet ) может быть задан, и он также будет использоваться для любых вложенных вызовов @testset . Заданные параметры применяются только к тому набору тестов, где они заданы. Тип набора тестов по умолчанию не принимает никаких параметров.

Строка описания принимает интерполяцию из индексов цикла. Если описание не указано, оно генерируется на основе переменных.

По умолчанию макрос @testset вернет сам объект набора тестов, хотя это поведение можно настроить в других типах наборов тестов. Если используется цикл for , макрос собирает и возвращает список возвращаемых значений метода finish , который по умолчанию возвращает список объектов наборов тестов, используемых в каждом итерационном шаге.

исходный код

Мы можем поместить наши тесты для функции foo(x) в набор тестов:

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

Наборы тестов также могут быть вложенными:

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(ones(i)) == i^2
           end
       end;
Test Summary: | Pass  Total
Foo Tests     |    8      8

В случае, если вложенный набор тестов не содержит неудач, как это произошло здесь, он будет скрыт в сводном отчете. Если у нас есть неудачный тест, будут показаны только подробности о неудачных наборах тестов:

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(ones(4)) == 15
           end
       end

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

Другие макросы тестов

Так как вычисления с плавающей точкой могут быть неточными, вы можете выполнить проверки приблизительного равенства, используя либо @test a ≈ b (где ≈, вводимое с помощью автодополнения \approx, является функцией isapprox()) или использовать isapprox() напрямую.

julia> @test 1 ≈ 0.999999999
Test Passed

julia> @test 1 ≈ 0.999999
Test Failed
  Expression: 1 ≈ 0.999999
   Evaluated: 1 ≈ 0.999999
ERROR: There was an error during testing

Base.Test.@inferredМакрос

@inferred f(x)

Проверяет, что выражение вызова f(x) возвращает значение того же типа, что и тип, выведенный компилятором. Это полезно для проверки стабильности типов.

f(x) может быть любым выражением вызова. Возвращает результат f(x) если типы совпадают, и Error Result если обнаруживаются разные типы.

julia> using Base.Test

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

julia> typeof(f(1,2,3))
Int64

julia> @code_warntype f(1,2,3)
Variables:
  #self# <optimized out>
  a <optimized out>
  b::Int64
  c <optimized out>

Body:
  begin
      unless (Base.slt_int)(1, b::Int64)::Bool goto 3
      return 1
      3:
      return 1.0
  end::UNION{FLOAT64, INT64}

julia> @inferred f(1,2,3)
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
Stacktrace:
 [1] error(::String) at ./error.jl:21

julia> @inferred max(1,2)
2
исходный код

Base.Test.@test_warnМакрос

@test_warn msg expr

Проверяет, приводит ли вычисление expr к выводу в STDERR, содержащему строку msg или соответствующему регулярному выражению msg. Если msg — это булева функция, проверяет, возвращает ли msg(output) значение true. Если msg — кортеж или массив, проверяет, содержит ли вывод об ошибке каждый элемент в msg. Возвращает результат вычисления expr.

См. также @test_nowarn для проверки отсутствия вывода об ошибке.

исходный код

Base.Test.@test_nowarnМакрос

@test_nowarn expr

Проверяет, приводит ли вычисление expr к пустому выводу в STDERR (без предупреждений или других сообщений). Возвращает результат вычисления expr.

исходный код

Неисправные тесты

Если тест постоянно терпит неудачу, его можно изменить, используя макрос @test_broken(). Это обозначит тест как Broken если тест продолжает терпит неудачу и уведомит пользователя через Error если тест пройден.

Base.Test.@test_brokenМакрос

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

Указывает тест, который должен пройти, но в настоящее время постоянно терпит неудачу. Проверяет, что выражение ex вычисляется до false или вызывает исключение. Возвращает Broken Result если это так, или Error Result если выражение вычисляется до true.

Форма @test_broken f(args...) key=val... работает так же, как для макроса @test.

исходный код

@test_skip() также доступен для пропуска теста без оценки, но учитывая пропущенный тест в отчете набора тестов. Тест не будет выполнен, но выдаст Broken Result.

Base.Test.@test_skipМакрос

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

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

Форма @test_skip f(args...) key=val... работает так же, как для макроса @test.

исходный код

Создание настраиваемых типов AbstractTestSet

Пакеты могут создавать свои собственные подтипы AbstractTestSet , реализуя методы record и finish . Подтип должен иметь одноаргументный конструктор, принимающий строку описания, а все параметры передавать в качестве ключевых аргументов.

Base.Test.recordФункция

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

Запишите результат в наборе тестов. Эта функция вызывается инфраструктурой @testset каждый раз, когда завершается содержащий @test макрос, и получает результат теста (который может быть Error). Это также будет вызвано с Error , если исключение было сгенерировано внутри блока теста, но вне контекста @test.

исходный код

Base.Test.finishФункция

finish(ts::AbstractTestSet)

Выполните все необходимые заключительные операции для данного набора тестов. Это вызывается инфраструктурой @testset после выполнения блока теста. Одним из распространённых применений этой функции является запись набора тестов в список результатов родительского элемента, используя get_testset.

исходный код

Base.Test отвечает за поддержание стека вложенных наборов тестов по мере их выполнения, но любая аккумуляция результатов — ответственность подтипа AbstractTestSet. Вы можете получить доступ к этому стеку с помощью методов get_testset и get_testset_depth. Обратите внимание, что эти функции не экспортируются.

Base.Test.get_testsetФункция

get_testset()

Получить активный набор тестов из локального хранилища задачи. Если активный набор тестов отсутствует, используется резервный набор тестов по умолчанию.

исходный код

Base.Test.get_testset_depthФункция

get_testset_depth()

Возвращает количество активных наборов тестов, не включая набор тестов по умолчанию.

исходный код

Base.Test также гарантирует, что вложенные вызовы @testset используют тот же подтип AbstractTestSet , что и их родитель, если не указано явно. Она не распространяет свойства набора тестов. Поведение наследования опций может быть реализовано пакетами, использующими инфраструктуру стека, предоставляемую Base.Test.

Определение базового подтипа AbstractTestSet может выглядеть так:

import Base.Test: record, finish
using Base.Test: AbstractTestSet, Result, Pass, Fail, Error
using Base.Test: get_testset_depth, get_testset
struct CustomTestSet <: Base.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)
    end
    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

© 2009–2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/release-0.6/stdlib/test/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API