Ricerca…


Sintassi

  • @test [expr]
  • @test_throws [Exception] [expr]
  • @testset "[nome]" inizia; [test]; fine
  • Pkg.test ([pacchetto])

Osservazioni

La documentazione della libreria standard per Base.Test copre materiale aggiuntivo oltre a quello mostrato in questi esempi.

Test di un pacchetto

Per eseguire i test unitari per un pacchetto, utilizzare la funzione Pkg.test . Per un pacchetto denominato MyPackage , il comando sarebbe

julia> Pkg.test("MyPackage")

Un risultato atteso sarebbe simile a

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

anche se ovviamente, non ci si può aspettare che corrisponda esattamente a quanto sopra, poiché i diversi pacchetti usano quadri diversi.

Questo comando esegue il file test/runtests.jl del pacchetto in un ambiente pulito.

Si può testare tutti i pacchetti installati contemporaneamente con

julia> Pkg.test()

ma questo di solito richiede molto tempo.

Scrivere un semplice test

I test unitari sono dichiarati nel file test/runtests.jl in un pacchetto. In genere, questo file ha inizio

using MyModule
using Base.Test

L'unità di base di test è la macro @test . Questa macro è come una sorta di asserzione. Qualsiasi espressione booleana può essere testata nella macro @test :

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

Possiamo provare la macro @test nella 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

La macro di test può essere utilizzata praticamente ovunque, ad esempio nei loop o nelle funzioni:

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

Scrivere un set di prova

0.5.0

Nella versione v0.5, i set di test sono incorporati nel modulo Base.Test libreria standard e non devi fare nulla di speciale (oltre a using Base.Test ) per utilizzarli.

0.4.0

I set di test non fanno parte della libreria Base.Test di Julia v0.4. Invece, è necessario REQUIRE il modulo BaseTestNext e aggiungere using BaseTestNext al file. Per supportare entrambe le versioni 0.4 e 0.5, è possibile utilizzare

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

È utile raggruppare @test s correlati in un set di test. Oltre a un'organizzazione di test più chiara, i set di test offrono risultati migliori e maggiore personalizzazione.

Per definire un set di test, è sufficiente racchiudere qualsiasi numero di @test s con un blocco @testset :

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

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

L'esecuzione di questi set di test stampa il seguente output:

Test Summary: | Pass  Total
  +           |    2      2

Test Summary: | Pass  Total
  *           |    2      2

Anche se un set di test contiene un test non funzionante, l'intero set di test verrà eseguito fino al completamento e i guasti verranno registrati e riportati:

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

Esecuzione di questo risultato del test in

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

I set di test possono essere annidati, consentendo un'organizzazione arbitrariamente profonda

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

Se i test superano, questo mostrerà solo i risultati per il set di test più esterno:

Test Summary: | Pass  Total
  Int         |    3      3

Ma se i test falliscono, viene riportato un drill-down nell'esatta serie di test e test che ha causato l'errore.

La macro @testset può essere utilizzata con un ciclo for per creare molti set di test contemporaneamente:

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

quali rapporti

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

Una struttura comune è quella di avere test o gruppi di test esterni. All'interno di questi set di test esterni, il test interno stabilisce il comportamento di test. Ad esempio, supponiamo di aver creato un tipo UniversalSet con un'istanza singleton che contiene tutto. Prima ancora di implementare il tipo, possiamo utilizzare i principi di sviluppo basati sui test e implementare i test:

@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

Possiamo quindi iniziare a implementare le nostre funzionalità fino a quando non supererà i nostri test. Il primo passo è definire il tipo:

immutable UniversalSet <: Base.AbstractSet end

Solo due dei nostri test passano adesso. Possiamo implementare in :

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

Ciò rende anche alcuni dei nostri test di sottoinsieme. Tuttavia, il issubset ( ) non funziona per UniversalSet , perché il fallback tenta di eseguire iterazioni su elementi, cosa che non possiamo fare. Possiamo semplicemente definire una specializzazione che rende issubset tornare true per ogni insieme:

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

E ora passano tutti i nostri test!

Test delle eccezioni

Le eccezioni incontrate durante l'esecuzione di un test non supereranno il test e, se il test non si trova in un set di test, terminerà il test engine. Di solito, questa è una buona cosa, perché nella maggior parte delle situazioni le eccezioni non sono il risultato desiderato. Ma a volte, si vuole testare in modo specifico che venga sollevata una certa eccezione. La macro @test_throws facilita questo.

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

Se viene generata l'eccezione sbagliata, @test_throws fallirà ancora:

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

e se non viene lanciata alcuna eccezione, @test_throws fallirà anche:

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

Testing Equality approssimativo a virgola mobile

Qual è l'accordo con quanto segue?

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

L'errore è causato dal fatto che nessuno di 0.1 , 0.2 e 0.3 è rappresentato nel computer esattamente come quei valori - 1//10 , 2//10 e 3//10 . Invece, sono approssimati da valori molto vicini. Ma come visto nel fallimento del test sopra, quando si aggiungono due approssimazioni insieme, il risultato può essere un'approssimazione leggermente peggiore di quanto sia possibile. C'è molto di più in questo argomento che non può essere coperto qui.

Ma non siamo sfortunati! Per verificare che la combinazione di arrotondamento a un numero in virgola mobile e aritmetica in virgola mobile sia approssimativamente corretta, anche se non esatta, possiamo usare la funzione isapprox (che corrisponde all'operatore ). Quindi possiamo riscrivere il nostro test come

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

Ovviamente, se il nostro codice fosse completamente sbagliato, il test continuerà a rilevare che:

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

La funzione isapprox utilizza l'euristica in base alla dimensione dei numeri e alla precisione del tipo a virgola mobile per determinare la quantità di errore da tollerare. Non è appropriato per tutte le situazioni, ma funziona in gran parte, e fa un sacco di sforzi per implementare la propria versione di isapprox .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow