Saturday, April 14, 2012

Learning Perl with Testing

If you want to learn Perl, you will find many tutorials which teach you how to program. And then, when you want to learn testing, you will find other tutorials, which teach you only how to test.

Eventually, you will want to do test driven programming where you write code and tests and the same time.

Much better to learn to code and test at the same time right from the beginning.

Here is an example of how to do it in Perl.

binary.pl

#!/usr/bin/perl
# Recursion - convert decimal number to binary
# from Higher-Order Perl: Transforming Programs with Programs
sub binary {
my $n = shift;

return $n if $n == 0 || $n ==1;

my $k = int($n / 2);
my $b = $n % 2;

my $E = binary($k);
return $E . $b;
}

binary.t

require "binary.pl";
use Test::More;

is( binary(0), 0, "0 : 0" );
is( binary(1), 1, "1 : 1" );
is( binary(2), 10, "2 : 10" );
is( binary(3), 11, "3 : 11" );
is( binary(4), 100, "4 : 100" );
is( binary(5), 101, "5 : 101" );
is( binary(6), 110, "6 : 110" );
is( binary(7), 111, "7 : 111" );
is( binary(37), 100101, "37 : 100101" );

done_testing;

You can run it to see all the tests passing.
ok 1 - 0 : 0
ok 2 - 1 : 1
ok 3 - 2 : 10
ok 4 - 3 : 11
ok 5 - 4 : 100
ok 6 - 5 : 101
ok 7 - 6 : 110
ok 8 - 7 : 111
ok 9 - 37 : 100101
ok 10 - 0 : 0
ok 11 - 1 : 1
ok 12 - 2 : 10
ok 13 - 3 : 11
ok 14 - 4 : 100
ok 15 - 5 : 101
ok 16 - 6 : 110
ok 17 - 7 : 111
ok 18 - 37 : 100101

This tests that it works. Now, we think about how to break it. Lets add tests to see what our function does if we give it a negative number or a string as an input.
Lets assume that the function should return a 0 if the input is incorrect.

is( binary("a"), 0, "a : 0" );
is( binary(-1), 0, "-1 : 0" );

You see that the function fails like this:

not ok 19 - a : 0
# Failed test 'a : 0'
# at C:\Documents and Settings\others\My Documents\perl_stuff\recursion\dec_to
_bin.pl line 53.
# got: 'a'
# expected: '0'
not ok 20 - -1 : 0
# Failed test '-1 : 0'
# at C:\Documents and Settings\others\My Documents\perl_stuff\recursion\dec_to
_bin.pl line 54.
# got: '01'
# expected: '0'
1..20
# Looks like you failed 2 tests of 20.

So, you change your binary function to return a 0 if the input is not a positive integer, and you are done.

sub binary {
my $n = shift;

return 0 unless $n =~ /\d+/ && $n >= 0;
return $n if $n == 0 || $n ==1;

my $k = int($n / 2);
my $b = $n % 2;

my $E = binary($k);
return $E . $b;
}

6 comments:

Anonymous said...

I think this can be a good idea though one needs to think how to introduce it and how to make sure the test framework does not hide problems or introduces new ones.

Nitish said...

Thanks Gabor. Nice to know that you think it is a good idea.

andrej said...

Nice - where are the other 20+ pages that introduce other perl or testing concepts? :)

Nitish said...

Thanks andrej.
This was just an idea of how Perl can be taught, so that the learner becomes a good programmer quickly, instead of going through all the steps that we had to go through. I never meant it to be a tutorial.

Heikki said...

I totally agree. I've done that in the past two courses that I have organized. In addition to teaching best practices, it does help the students to focus on what they are supposed to be doing.

P.S. binary.pl needs a true perl statement in the end:

1;

Will said...

I didn't get a good grip on Python until I went through the "Koans" (borrowed from Ruby). It may not be for everyone, but having a Perl version of the Koans would be a great addition to the "learning Perl" initiative (and who know's it might already exist)

https://github.com/gregmalcolm/python_koans/wiki