Julia Language
Enhetstestning
Sök…
Syntax
- @ test [expr]
- @test_throws [Undantag] [expr]
- @testset "[namn]" börjar; [tester]; slutet
- Pkg.test ([paket])
Anmärkningar
Standardbiblioteksdokumentationen för Base.Test
täcker ytterligare material utöver vad som visas i dessa exempel.
Testa ett paket
För att köra enhetstesterna för ett paket använder Pkg.test
funktionen Pkg.test
. För ett paket som heter MyPackage
skulle kommandot vara
julia> Pkg.test("MyPackage")
En förväntad produktion skulle likna
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
men uppenbarligen kan man inte förvänta sig att det matchar ovanstående exakt, eftersom olika paket använder olika ramverk.
Detta kommando kör paketets test/runtests.jl
fil i en ren miljö.
Man kan testa alla installerade paket samtidigt med
julia> Pkg.test()
men det tar vanligtvis mycket lång tid.
Att skriva ett enkelt test
Enhetstester deklareras i test/runtests.jl
filen i ett paket. Vanligtvis börjar den här filen
using MyModule
using Base.Test
Den grundläggande @test
är @test
. Detta makro är som ett påstående av olika slag. Alla booleska uttryck kan testas i @test
test- @test
:
@test 1 + 1 == 2
@test iseven(10)
@test 9 < 10 || 10 < 9
Vi kan testa @test
i 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
Testmakroet kan användas på nästan var som helst, till exempel i slingor eller funktioner:
# 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)
Skriva en testuppsättning
I version v0.5 är testuppsättningar inbyggda i standardbiblioteket Base.Test
modulen, och du behöver inte göra något speciellt (förutom att using Base.Test
) för att använda dem.
Testuppsättningar ingår inte i Julia v0.4: s Base.Test
bibliotek. Istället måste du REQUIRE
BaseTestNext
modulen och lägga till using BaseTestNext
till din fil. För att stödja både version 0.4 och 0.5 kan du använda
if VERSION ≥ v"0.5.0-dev+7720"
using Base.Test
else
using BaseTestNext
const Test = BaseTestNext
end
Det är bra att gruppera relaterade @test
tillsammans i en testuppsättning. Förutom tydligare testorganisation erbjuder testuppsättningar bättre output och mer anpassningsbarhet.
För att definiera en testuppsättning ska du helt enkelt slå in valfritt antal @test
med ett @testset
block:
@testset "+" begin
@test 1 + 1 == 2
@test 2 + 2 == 4
end
@testset "*" begin
@test 1 * 1 == 1
@test 2 * 2 == 4
end
Att köra dessa testuppsättningar skriver ut följande utgång:
Test Summary: | Pass Total
+ | 2 2
Test Summary: | Pass Total
* | 2 2
Även om en testuppsättning innehåller ett misslyckande test, kommer hela testuppsättningen att köras till slut, och felen kommer att registreras och rapporteras:
@testset "-" begin
@test 1 - 1 == 0
@test 2 - 2 == 1
@test 3 - () == 3
@test 4 - 4 == 0
end
Att köra denna testuppsättning resulterar i
-: 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.
...
Testuppsättningar kan kapslas, vilket möjliggör godtycklig djup organisation
@testset "Int" begin
@testset "+" begin
@test 1 + 1 == 2
@test 2 + 2 == 4
end
@testset "-" begin
@test 1 - 1 == 0
end
end
Om testen klarar visar detta bara resultaten för den yttersta testuppsättningen:
Test Summary: | Pass Total
Int | 3 3
Men om testen misslyckas rapporteras en nedläggning i den exakta testuppsättningen och testet som orsakar felet.
@testset
kan användas med en for
loop för att skapa många testuppsättningar samtidigt:
@testset for i in 1:5
@test 2i == i + i
@test i^2 == i * i
@test i ÷ i == 1
end
som rapporterar
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
En gemensam struktur är att ha yttre testuppsättningar testkomponenter eller typer. Inom dessa yttre testuppsättningar anger inre testtestbeteende. Anta till exempel att vi skapade en typ UniversalSet
med en singleton-instans som innehåller allt. Innan vi ens implementerar typen kan vi använda testdrivna utvecklingsprinciper och implementera testerna:
@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
Vi kan sedan börja implementera vår funktionalitet tills den klarar våra tester. Det första steget är att definiera typen:
immutable UniversalSet <: Base.AbstractSet end
Endast två av våra test klarar just nu. Vi kan implementera in
:
immutable UniversalSet <: Base.AbstractSet end
Base.in(x, ::UniversalSet) = true
Detta gör också att några av våra delmätningstester klarar. Emellertid issubset
( ⊆
) fallback inte för UniversalSet
, eftersom fallbacken försöker iterera över element, vilket vi inte kan göra. Vi kan helt enkelt definiera en specialisering som gör att issubset
true
för alla uppsättningar:
immutable UniversalSet <: Base.AbstractSet end
Base.in(x, ::UniversalSet) = true
Base.issubset(x::Base.AbstractSet, ::UniversalSet) = true
Och nu klarar alla våra test!
Testa undantag
Undantag som uppstår vid körning av ett test kommer att misslyckas med testet, och om testet inte finns i en testuppsättning, avsluta testmotorn. Vanligtvis är detta bra, för i de flesta situationer är undantag inte det önskade resultatet. Men ibland vill man testa specifikt att ett visst undantag tas upp. @test_throws
underlättar detta.
julia> @test_throws BoundsError [1, 2, 3][4]
Test Passed
Expression: ([1,2,3])[4]
Thrown: BoundsError
Om fel undantag kastas kommer @test_throws
fortfarande att misslyckas:
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
och om inget undantag kastas kommer @test_throws
att misslyckas också:
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
Testa flytande punkt Ungefärlig jämlikhet
Vad är det med följande?
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
Felet orsakas av att ingen av 0.1
, 0.2
och 0.3
representeras i datorn som exakt dessa värden - 1//10
, 2//10
och 3//10
. Istället är de ungefärliga av värden som är mycket nära. Men som framgår av testfelet ovan kan resultatet, när man lägger till två approximationer tillsammans, vara en något sämre tillnärmning än vad som är möjligt. Det finns mycket mer till detta ämne som inte kan behandlas här.
Men vi har ingen tur! För att testa att kombinationen av avrundning till ett flytande punktnummer och flyttalsaritmetik är ungefär korrekt, även om inte exakt, kan vi använda isapprox
funktionen (som motsvarar operatören ≈
). Så vi kan skriva om vårt test som
julia> @test 0.1 + 0.2 ≈ 0.3
Test Passed
Expression: 0.1 + 0.2 ≈ 0.3
Evaluated: 0.30000000000000004 isapprox 0.3
Naturligtvis, om vår kod var helt fel, kommer testet fortfarande att fånga det:
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
funktionen använder heuristik baserat på storleken på siffrorna och precisionen för flytpunkttypen för att bestämma mängden fel som ska tolereras. Det är inte lämpligt för alla situationer, men det fungerar i de flesta och sparar mycket ansträngning för att genomföra sin egen version av isapprox
.