Buscar..


Sintaxis

  • @test [expr]
  • @test_throws [Exception] [expr]
  • @testset "[nombre]" comienza; [pruebas]; fin
  • Pkg.test ([paquete])

Observaciones

La documentación estándar de la biblioteca para Base.Test cubre material adicional más allá de lo que se muestra en estos ejemplos.

Probando un paquete

Para ejecutar las pruebas unitarias de un paquete, use la función Pkg.test . Para un paquete llamado MyPackage , el comando sería

julia> Pkg.test("MyPackage")

Una salida esperada sería similar 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

aunque obviamente, no se puede esperar que coincida exactamente con lo anterior, ya que los paquetes diferentes utilizan marcos diferentes.

Este comando ejecuta el archivo test/runtests.jl del paquete en un entorno limpio.

Uno puede probar todos los paquetes instalados a la vez con

julia> Pkg.test()

pero esto usualmente toma mucho tiempo.

Escribir una prueba simple

Las pruebas unitarias se declaran en el archivo test/runtests.jl en un paquete. Por lo general, este archivo comienza.

using MyModule
using Base.Test

La unidad básica de prueba es la macro @test . Esta macro es como una aseveración de clases. Cualquier expresión booleana se puede probar en la macro @test :

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

Podemos probar la macro @test en el 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 de prueba se puede utilizar en casi cualquier lugar, como en bucles o funciones:

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

Escribir un conjunto de prueba

0.5.0

En la versión v0.5, los conjuntos de pruebas están integrados en el módulo estándar de Base.Test biblioteca, y no tiene que hacer nada especial (además de using Base.Test ) para usarlos.

0.4.0

Los conjuntos de pruebas no forman parte de la biblioteca Base.Test de Julia v0.4. En su lugar, debe REQUIRE el módulo BaseTestNext y agregar using BaseTestNext a su archivo. Para soportar tanto la versión 0.4 como la 0.5, podrías usar

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

Es útil agrupar @test relacionados en un conjunto de prueba. Además de una organización de pruebas más clara, los conjuntos de pruebas ofrecen mejores resultados y más personalización.

Para definir un conjunto de pruebas, simplemente envuelva cualquier número de @test s con un bloque @testset :

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

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

La ejecución de estos conjuntos de pruebas imprime el siguiente resultado:

Test Summary: | Pass  Total
  +           |    2      2

Test Summary: | Pass  Total
  *           |    2      2

Incluso si un conjunto de pruebas contiene una prueba que falla, todo el conjunto de pruebas se ejecutará hasta su finalización, y las fallas se registrarán e informarán:

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

Ejecutando los resultados de este conjunto de pruebas en

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

Los conjuntos de pruebas se pueden anidar, lo que permite una organización arbitrariamente profunda

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

Si las pruebas pasan, esto solo mostrará los resultados del conjunto de pruebas más externo:

Test Summary: | Pass  Total
  Int         |    3      3

Pero si las pruebas fallan, se informa un desglose en el conjunto de pruebas exacto y la prueba que causa el fallo.

La macro @testset se puede usar con un bucle for para crear muchos conjuntos de pruebas a la vez:

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

que informa

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 estructura común es tener conjuntos de prueba externos componentes o tipos de prueba. Dentro de estos conjuntos de pruebas externas, los conjuntos de pruebas internas prueban el comportamiento. Por ejemplo, supongamos que creamos un tipo UniversalSet con una instancia de singleton que contiene todo. Antes de que implementemos el tipo, podemos usar principios de desarrollo controlados por pruebas e implementar las pruebas:

@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

Entonces podemos comenzar a implementar nuestra funcionalidad hasta que pase nuestras pruebas. El primer paso es definir el tipo:

immutable UniversalSet <: Base.AbstractSet end

Sólo dos de nuestras pruebas pasan ahora. Podemos implementar in :

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

Esto también hace que algunas de nuestras pruebas de subconjunto pasen. Sin embargo, el issubset ( ) no funciona para UniversalSet , porque el respaldo trata de iterar sobre elementos, lo que no podemos hacer. Simplemente podemos definir una especialización que hace que issubset devuelva true para cualquier conjunto:

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

Y ahora, todas nuestras pruebas pasan!

Pruebas de excepciones

Las excepciones encontradas durante la ejecución de una prueba fallarán en la prueba, y si la prueba no está en un conjunto de pruebas, finalice la prueba del motor. Por lo general, esto es bueno, porque en la mayoría de las situaciones las excepciones no son el resultado deseado. Pero a veces, uno quiere probar específicamente que se produce una cierta excepción. La macro @test_throws facilita esto.

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

Si se lanza la excepción incorrecta, @test_throws seguirá fallando:

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

y si no se produce ninguna excepción, @test_throws también fallará:

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

Prueba de Punto Flotante Aproximada Igualdad

¿Cuál es el trato con lo siguiente?

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

El error se debe al hecho de que ninguno de 0.1 , 0.2 y 0.3 está representado en la computadora exactamente como esos valores: 1//10 , 2//10 y 3//10 . En cambio, se aproximan por valores que están muy cerca. Pero como se vio en el fallo de la prueba anterior, al sumar dos aproximaciones, el resultado puede ser una aproximación un poco peor de lo que es posible. Hay mucho más sobre este tema que no se puede cubrir aquí.

¡Pero no estamos sin suerte! Para probar que la combinación de redondeo a un número de punto flotante y la aritmética de punto flotante es aproximadamente correcta, aunque no exacta, podemos utilizar el isapprox función (que corresponde al operador ). Así que podemos reescribir nuestra prueba como

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

Por supuesto, si nuestro código estaba completamente equivocado, la prueba todavía detectará eso:

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 función isapprox utiliza heurísticas basadas en el tamaño de los números y la precisión del tipo de punto flotante para determinar la cantidad de error a tolerar. No es apropiado para todas las situaciones, pero funciona en la mayoría, y ahorra mucho esfuerzo al implementar la propia versión de isapprox .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow