Szukaj…


Składnia

  • @test [expr]
  • @test_throws [Exception] [expr]
  • @testset „[name]” start; [testy]; koniec
  • Pkg.test ([pakiet])

Uwagi

Standardowa dokumentacja biblioteki dla Base.Test obejmuje dodatkowe materiały poza tymi pokazanymi w tych przykładach.

Testowanie pakietu

Aby uruchomić testy jednostkowe dla pakietu, użyj funkcji Pkg.test . W przypadku pakietu o nazwie MyPackage będzie to polecenie

julia> Pkg.test("MyPackage")

Oczekiwany wynik byłby podobny do

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

choć oczywiście nie można oczekiwać, że będzie dokładnie pasować do powyższego, ponieważ różne pakiety używają różnych frameworków.

To polecenie uruchamia plik test/runtests.jl w czystym środowisku.

Można przetestować wszystkie zainstalowane pakiety jednocześnie

julia> Pkg.test()

ale zwykle zajmuje to bardzo dużo czasu.

Pisanie prostego testu

Testy jednostkowe są deklarowane w pliku test/runtests.jl w pakiecie. Zazwyczaj ten plik się zaczyna

using MyModule
using Base.Test

Podstawową jednostką testowania jest makro @test . To makro jest swego rodzaju stwierdzeniem. Dowolne wyrażenie logiczne można przetestować w makrze @test :

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

Możemy wypróbować makro @test w 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

Makra testowego można używać niemal wszędzie, na przykład w pętlach lub funkcjach:

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

Pisanie zestawu testowego

0,5.0

W wersji v0.5 zestawy testowe są wbudowane w standardowy moduł biblioteki Base.Test i nie trzeba nic specjalnego robić (oprócz using Base.Test ), aby z nich korzystać.

0.4.0

Zestawy testowe nie są częścią biblioteki Base.Test Julii Base.Test . Zamiast tego, trzeba REQUIRE się BaseTestNext moduł i dodać using BaseTestNext do pliku. Aby obsługiwać zarówno wersję 0.4, jak i 0.5, możesz użyć

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

Pomocne jest grupowanie powiązanych @test w zestawie testowym. Oprócz bardziej przejrzystej organizacji testów, zestawy testowe oferują lepszą wydajność i więcej możliwości dostosowywania.

Aby zdefiniować zestaw testowy, po prostu @test dowolną liczbę @test @testset blokiem @testset :

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

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

Uruchomienie tych zestawów testowych powoduje wydrukowanie następujących danych wyjściowych:

Test Summary: | Pass  Total
  +           |    2      2

Test Summary: | Pass  Total
  *           |    2      2

Nawet jeśli zestaw testowy zawiera test zakończony niepowodzeniem, cały zestaw testowy zostanie uruchomiony do końca, a awarie zostaną zarejestrowane i zgłoszone:

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

Uruchomienie tego zestawu testowego powoduje

-: 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.
    ...

Zestawy testowe można zagnieżdżać, co pozwala na dowolną głęboką organizację

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

Jeśli testy przejdą pomyślnie, zostaną wyświetlone tylko wyniki dla najbardziej zewnętrznego zestawu testowego:

Test Summary: | Pass  Total
  Int         |    3      3

Ale jeśli testy zakończą się niepowodzeniem, zgłaszane jest zebranie dokładnego zestawu testów i testu powodującego błąd.

@testset można używać z pętlą for do tworzenia wielu zestawów testowych jednocześnie:

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

które raporty

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

Powszechną strukturą jest posiadanie zewnętrznych zestawów testowych komponentów lub typów testowych. W ramach tych zewnętrznych zestawów testowych testy zachowania wewnętrznych zestawów testowych. Załóżmy na przykład, że stworzyliśmy typ UniversalSet z instancją singleton, która zawiera wszystko. Zanim jeszcze zaimplementujemy ten typ, możemy zastosować zasady programistyczne oparte na testach i zaimplementować testy:

@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

Następnie możemy rozpocząć wdrażanie naszej funkcjonalności, dopóki nie przejdzie ona pomyślnie naszych testów. Pierwszym krokiem jest zdefiniowanie typu:

immutable UniversalSet <: Base.AbstractSet end

Tylko dwa z naszych testów przeszły teraz. Możemy wdrożyć in :

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

To sprawia, że niektóre z naszych testów podzestawów muszą przejść pomyślnie. issubset ( ) nie działa jednak w UniversalSet , ponieważ zastępowanie próbuje iterować elementy, czego nie możemy zrobić. Możemy po prostu zdefiniować specjalizację, która sprawi, że issubset zwróci wartość true dla dowolnego zestawu:

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

A teraz wszystkie nasze testy przeszły pomyślnie!

Testowanie wyjątków

Wyjątki napotkane podczas uruchamiania testu zakończą się niepowodzeniem, a jeśli test nie znajduje się w zestawie testowym, zakończ silnik testowy. Zwykle jest to dobra rzecz, ponieważ w większości sytuacji wyjątki nie są pożądanym rezultatem. Ale czasami chce się konkretnie przetestować, czy powstaje pewien wyjątek. @test_throws makro @test_throws .

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

Jeśli @test_throws zostanie niewłaściwy wyjątek, @test_throws nadal się nie powiedzie:

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

a jeśli nie zostanie @test_throws żaden wyjątek, @test_throws również się nie powiedzie:

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

Testowanie przybliżonej równości zmiennoprzecinkowej

O co chodzi z następującymi?

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

Błąd jest spowodowany faktem, że żadne z 0.1 , 0.2 i 0.3 są reprezentowane w komputerze jako dokładnie te wartości - 1//10 , 2//10 i 3//10 . Zamiast tego są one aproksymowane przez wartości, które są bardzo zbliżone. Ale jak widać w powyższym niepowodzeniu testu, po dodaniu dwóch przybliżeń razem wynik może być nieco gorszy niż to możliwe. Jest o wiele więcej w tym temacie , czego nie można tutaj omówić.

Ale nie brakuje nam szczęścia! Aby sprawdzić, czy połączenie zaokrąglania do liczby zmiennoprzecinkowej i arytmetyki zmiennoprzecinkowej jest w przybliżeniu prawidłowe, nawet jeśli nie jest dokładne, możemy użyć funkcji isapprox (która odpowiada operatorowi ). Możemy więc przepisać nasz test jako

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

Oczywiście, jeśli nasz kod był całkowicie niepoprawny, test nadal wykryje, że:

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

Funkcja isapprox wykorzystuje heurystykę opartą na wielkości liczb i precyzji typu zmiennoprzecinkowego, aby określić wielkość błędu do tolerowania. Nie jest odpowiedni dla wszystkich sytuacji, ale działa w większości i oszczędza dużo wysiłku przy wdrażaniu własnej wersji isapprox .



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow