2011-02-09 15 views
5

OK, przedmowa to pytanie jest potencjalnie "głupsze" niż mój normalny poziom pytania - jednak ten problem denerwuje mnie od kilku ostatnich dni, więc i tak o to zapytam. Podam próbny przykład mojego problemu, więc mogę mieć nadzieję na uogólnienie go na mój obecny problem.Perl, stringi, spływy, testy jednostkowe i regexpy!

#!/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'); 

Teraz, kiedy ten jest prowadzony otrzymuję komunikat

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

, aby prace testowy - ja też odkomentować skomentował linię lub użyj is ($price, '1.50', 'Great Success'). Obie opcje nie działają dla mnie - testuję ogromną ilość zagnieżdżonych danych przy użyciu Testu :: Głębokie i cmp_deeply. Moje pytanie brzmi: jak wyodrębnić podwójne z regexp, a następnie użyć go natychmiast jako podwójne - lub jeśli istnieje lepszy sposób w ogóle daj mi znać - i nie krępuj się powiedzieć mi, aby podjąć ogrodnictwo lub coś lol, nauka Perl jest ciężko.

+0

Porównujesz ciąg znaków z liczbą, więc są one porównywane jako ciągi. Nie sądzę, że możesz z tym zrobić. –

Odpowiedz

10

Korzystasz już Test::Deep, więc można po prostu użyć num() owijki wykonaj porównanie numeryczne zamiast ciągłego (pozwala nawet dodać tolerancję, aby porównać dwie nieścisłe wartości zmiennoprzecinkowe):

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

Dla normalnych porównań wykonanych osobno, cmp_ok zadziała, ale num() jest nadal dostępny: cmp_deeply($value, num(1.5), 'test name') nadal działa.

+1

Dokładnie to, czego szukałem, wielkie dzięki. –

1

Siła $price należy interpretować jako liczbę:

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

@ Platinum Azure: Nie, naprawdę musisz dodać numer. Sprawdź to. – btilly

+0

Yup, ponownie przyjrzałem się Perlopowi, żeby się upewnić i masz rację. Może myślę o Perlu 6 lub czymś podobnym. –

+1

Jak napisałem w moim poście, nie mogę efektywnie korzystać z ceny 0 + $, ponieważ używam cmp_deeply - do zrobienia ceny 0 + $, musiałbym przejść przez cały mój obecny kod i zrobić to dla każdej zmiennej, która powinno być podwójne. –

0

Powodem takiego zachowania jest to, że wykorzystuje eq robić porównań, co zmusza stringification swoich argumentów. 1.50 oznacza "1.5", a to się nie powiedzie.

Twoje wybory to żyć z zachowaniem (wymuszenie siłą lub numifikacja) lub napisać własną alternatywę, która porówna liczbowo, jeśli obie strony będą wyglądały jak liczby, zanim spadną na porównania eq. Osobiście pójdę z tym drugim podejściem.

1

Dlaczego nie skorzystać z wypróbowanej i prawdziwej ok? Będziesz testować to, co naprawdę chcesz przetestować, i nie musisz się martwić, czy robisz coś zbyt subtelnego lub zbyt sprytnego.

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

is ma zapewnić dodatkowe diagnostykę awarii, ale to jest dość łatwe do zrobienia z ok też

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

'is' dostarcza oczekiwane i podane wartości w przypadku nieudanego potwierdzenia, którego" ok "nie można wykonać. – Tim

1

Twoje testy zawodzą bo is($x, $y, $name) jest równoważna cmp_ok($x, 'eq', $y, $name). eq zmusza każdy z jego argumentów do oceny jako łańcuchy. Ponieważ chcesz mieć równość numeryczną, możesz napisać ją przy pomocy cmp_ok używając '=='. Można ułatwić pisząc własną wersję numerycznej is:

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

Ale ta wersja jest subtelnie uszkodzony, będzie zgłaszać błędy na niewłaściwych liniach. Aby upewnić się, że raportowanie błędów pokazuje odpowiednie linie:

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

Powodem goto &sub dlatego cmp_ok wykorzystuje caller określić, gdzie się błąd. Składnia goto &sub usuwa konfigurację ramki wywołania dla is_num, więc cmp_ok myśli, że została wywołana z lokalizacji, która była is_num.

Wreszcie korek mojego modułu Test::Magic który zapewnia cukier syntaktyczny dla Test::More:

use Test::Magic 'no_plan'; 

... # setup code 

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

który jest interpretowany jako cmp_ok($price, '==', 1.50, 'fruit price')

+0

Zamiast 'goto', powinieneś zamiast tego' local $ Test :: Builder :: Level = $ Test :: Builder :: Level + 1; 'zmienić miejsce, w którym raport o awarii został zgłoszony. – Ether

+0

@Ether => po co używać rozwiązania specyficznego dla interfejsu API, gdy Perl zapewnia ogólny sposób wysyłania w sposób przezroczysty? Istnieje kilka sytuacji, w których wymagane jest ustawienie '$ Test :: Builder :: Level' (uruchamianie wielu testów z jednej strony wywołania lub wykonywanie czynności po testowaniu), ale to nie jest jedna z nich. –