Testing

Matts on 2002-12-11T16:47:59

Testing just saved my ass once more.

I had discovered that for some reason I was only catching about 70% of spam with my code. This worried me a lot. And I've been trying to figure out for a couple of days now what was wrong...

So today I wrote a test script that compared the parsing of mails using my custom mail parser with a different one. Turns out some subtle bugs were causing me to miss boat loads of email.

The good thing is the bug was in my test harness, not in the actual code, so while I found and fixed lots of interesting differences, I also managed to fix my test harness to give me the results I'm after.

In the process I got to use Test::More.

Things I like:

- More sensible function names than Test.pm's ok() functions - is_deeply() - this rocks.

Things I didn't like much:

- I found it a bit convoluted to write tests where the number of tests depends on some external data source (e.g. in my case the number of files in a directory). Solutions range from BEGIN blocks to ->import tricks, but none are as simple as: plan tests => $var;

So all in all I prefer it to Test.pm, but I'm unlikely to use it unless I require it for some reason (such as is_deeply() in this case). Simply because I prefer to keep prereqs to a minimum where I can. Luckily this case is for an internal thing, so I can control the prereq's.

Anyway, kudos to Schwern (and anyone else who helped) for turning Perl into the most testable platform on the planet.

Update: D'oh. I meant Test::More. Of course I've used Test::Harness before. I'd be a fool to have over 50 CPAN modules and not used it :-)


Testing

davorg on 2002-12-11T17:19:34

turning Perl into the most testable platform on the planet

This is an excellent point. I wonder if anyone has used Perl's new improved testing features to persuade their bosses to use Perl over some other language.

How do other languages stack up against Perl in testability?

Re:Testing

djberg96 on 2002-12-11T20:12:55

Ruby's Test::Unit (by Nathanial Talbott) works well. I haven't used either one very extensively, but I think they're about on par with each other. I've used it to help me catch some bugs while porting Spreadsheet::WriteExcel.

Someone feel free to disagree...

Re:Testing

Matts on 2002-12-11T21:35:37

I don't want to disagree, but I still think Ruby has a long way to go in this respect. It still has no unified build framework (unless this has changed recently), and since it doesn't have the above, I'm assuming it doesn't have a unified test harness framework.

While ruby's Test::Unit looks vaguely interesting, it doesn't look nearly as simple as Test::Harness + Test.pm (or Test::More). The thing about Test::Harness is that it's not a module, but a tool. And a nice tool at that - it shows me test progress, and when I get test errors it shows them as I'm progressing, so that if I want to I can ctrl-C the tests and fix them.

I don't know for sure if the ruby stuff doesn't provide this, so I could be blowing bubbles here. Please correct me if I'm wrong.

I guess I just want CPAN for ruby. Once that's there, all this stuff kind of happens as a consequence of that.

Re:Testing

perigrin on 2002-12-12T10:50:33

Yes, but don't we all just want CPAN for [insert non-perl lanugauge here]?

If you like is_deeply, see also Test::Differences

barries on 2002-12-11T17:30:25

Even real tigers prefer ir to meat.

See also Test::More's no_plan option

barries on 2002-12-11T17:31:27

For testing with a variable number of tests.

Graphical test failure hotspot diagrams

barries on 2002-12-11T17:35:45

And here's a neat page that shows how to map out your code coverage for failing and passing tests to show code that is likely to be involved with failed tests:

http://www.cc.gatech.edu/aristotle/Tools/tarantula/

Now, when I break a test, I usually know what I changed, so I'm not sure how useful this is for me. For the automated perl smoke tests, it could be cool, though the profiling bit would slow the tests down a lot.

Anyway, just some cool stuff. See the related work on that site about using antialiasing for displaying massive amounts of data (including overviews of huge text documents) in fixed size windows.

- Barrie

Planning

Dom2 on 2002-12-11T18:02:28

But the syntax you gave is exactly the syntax for plan under Test::More:
use Test::More;

opendir( D, '/etc' ) or die $!;
my @files = grep { -f } readdir( D );

plan tests => @files;

# ...

See, nice and simple. No BEGIN blocks required. And, as somebody else mentioned, there's always no_plan if you're feeling lazy.

-Dom

Test::More

Ovid on 2002-12-11T18:12:02

Have you considered using Test::More? It has much more useful testing facilities and it allows you to name tests, which can give you very descriptive testing information. Further, if a test fails, it often provides very useful diagnostic information such as telling you what value you were expecting in $foo and what value $foo actually received. Here's a sample from some tests of a "People" object that I have.

#
# testing is_senior_contact()
#

$sql = <<"END_SQL";
SELECT people_id, category_id
FROM people_category
WHERE people_id = (SELECT min(people_id) FROM people_category)
END_SQL
$data = $db->_arrayref( $sql );
my ( $test_person, $test_category ) = @$data;

can_ok( $people->[0], 'is_senior_contact' );
$person = Foo::People->new( $test_person );
undef $data;
$data = $person->is_senior_contact( $test_category );
ok( $data, '... and a senior contact should return true' );
$data = $person->is_senior_contact( 0 );
ok( ! $data, '... and return false if they\'re not a senior contact' );

And this outputs:

ok 16 - Foo::People->can('is_senior_contact')
ok 17 - ... and a senior contact should return true
ok 18 - ... and return false if they're not a senior contact

If one of those tests fails, I find out instantly what test failed and why. This is a significant improvement over Test.pm.