2011-02-09 23 views
5

OK, como prefacio, esta pregunta es potencialmente 'más estúpida' que mi nivel normal de preguntas; sin embargo, este problema me ha molestado durante los últimos días, así que lo preguntaré de todos modos. Daré un ejemplo falso de cuál es mi problema, así que espero generalizarlo para mi problema actual.Perl, cuerdas, flotadores, pruebas unitarias y expresiones regulares!

#!/usr/bin/perl -w use strict; 

use Test::More 'no_plan'; 

my $fruit_string = 'Apples cost $1.50'; 
my ($fruit, $price) = $fruit_string =~ /(\w+)s cost \$(\d+\.\d+)/; 

# $price += 0; # Uncomment for Great Success 
is ($price, 1.50, 'Great Success'); 

Ahora, cuando esta se ejecuta me sale el mensaje

# Failed test 'Great Success' 
#   got: '1.50' 
#  expected: '1.5' 

Para hacer el trabajo de prueba - I cualquiera descomentar la línea comentada, o utiliza is ($price, '1.50', 'Great Success'). Ambas opciones no funcionan para mí: estoy probando una gran cantidad de datos anidados utilizando Test :: Deep y cmp_deeply. Mi pregunta es, ¿cómo se puede extraer un doble de una expresión regular y luego utilizarlo de inmediato como un doble, o si hay una mejor manera de hacerlo saberlo? Y no dude en decirme que ocupe la jardinería o algo así, aprender Perl es difícil.

+0

Está comparando la cuerda con el número, entonces se las compara como cuerdas. No creo que puedas hacer nada con eso. –

Respuesta

10

Usted ya está usando Test::Deep, por lo que sólo tiene que utilizar el contenedor a num() realice una comparación numérica en lugar de cadena (incluso le permite agregar una tolerancia, para comparar dos valores de punto flotante inexactos):

cmp_deeply(
    $result, 
    { 
     foo   => 'foo', 
     bar   => 'blah', 
     quantity => 3, 
     price  => num(1.5), 
    }, 
    'result hash is correct', 
); 

Para las comparaciones normales realizadas por separado, cmp_ok funcionará, pero num() sigue disponible: cmp_deeply($value, num(1.5), 'test name') todavía funciona.

+1

Exactamente lo que estaba buscando, muchas gracias. –

1

Fuerza $price a interpretarse como un número:

is (0 + $price, 1.50, 'Great Success'); 
+2

@ Platinum Azure: No, realmente necesita agregar el número. Pruébalo. – btilly

+0

Sí, miré a Perlop nuevamente solo para asegurarme y tienes razón. Tal vez estoy pensando en Perl 6 o algo así. –

+1

Como mencioné en mi publicación, no puedo usar 0 + $ precio efectivamente, ya que estoy usando cmp_deeply - para hacer 0 + $ precio, tendría que pasar por todo mi código actual y hacerlo a cada variable que debería ser un doble. –

0

La razón de este comportamiento es que se utiliza eq hacer comparaciones, lo que obliga stringification de sus argumentos. 1.50 stringifies a '1.5' y esto falla.

Sus elecciones son vivir con el comportamiento (forzar la cadena o la numeración) o escribir su propia alternativa a es la cual se comparará numéricamente si ambas partes parecen números antes de recurrir a comparaciones de eq. Yo personalmente iría con el último enfoque.

1

¿Por qué no utilizar el probado ok? Probará lo que realmente quiere probar y no tendrá que preocuparse de si is está haciendo algo demasiado sutil o demasiado inteligente.

ok($price == 1.5, 'Great Success'); 

is proporciona algunos diagnósticos adicionales en caso de fallo, pero que es bastante fácil de hacer con ok, también

ok($price == 1.5, 'Great Success') or diag("Expected \$price==1.5, got $price"); 
+1

'is' proporciona los valores esperados y dados en caso de una aserción fallida, lo que' ok' no puede hacer. – Tim

1

Sus pruebas están fallando debido a is($x, $y, $name) es equivalente a cmp_ok($x, 'eq', $y, $name). El eq obliga a cada uno de sus argumentos a evaluarse como cadenas. Como quiere igualdad numérica, puede escribirla con cmp_ok usando '=='. Se podría facilitar las cosas por escribir su propia versión numérica de is:

sub is_num {cmp_ok $_[0], '==', $_[1], $_[2]} 

Pero esa versión se ha roto de manera sutil, que informará de errores en las líneas equivocadas. Para asegurarse de que los informes de errores muestra la dirección correcta:

sub is_num {splice @_, 1, 0, '=='; goto &cmp_ok} 

La razón de la goto &sub es porque cmp_okcaller utiliza para determinar dónde ocurrió el error. La sintaxis goto &sub borra la configuración del cuadro de llamada para is_num, de modo que cmp_ok cree que se llama desde la ubicación en la que estaba is_num.

Por último, un tapón de mi módulo Test::Magic que proporciona el azúcar sintáctica para Test::More:

use Test::Magic 'no_plan'; 

... # setup code 

test 'fruit price', 
    is $price == 1.50; 

que se interpreta como cmp_ok($price, '==', 1.50, 'fruit price')

+0

En lugar de 'goto', en su lugar debe hacer' local $ Test :: Builder :: Level = $ Test :: Builder :: Level + 1; 'para modificar dónde se informa la prueba que falla. – Ether

+0

@Ether => ¿por qué utilizar una solución específica de API cuando Perl proporciona una forma general de envío de forma transparente? Hay algunas situaciones en las que se necesita establecer '$ Test :: Builder :: Level' (ejecutar varias pruebas desde un sitio de llamada o realizar acciones posteriores a la prueba), pero este no es uno de ellos. –