Testing Readable
Testing Readable
Testing Readable
Scott Wiersdorf
NTT/Verio
Assertion
I want to convince you that just as building a house without plans is
cobbling, not engineering, writing software without knowing in advance
how the software should work is also cobbling and not software
engineering.
This is not to say that you must
the software will precisely work
Because of the complex nature of
non-linearly, which allows us to
adjustments as we go.
or even how
project.
it
make
About Me (briefly)
A Testing Example
Tests
Documentation
Writing Code
Future Improvements
The Take-away
I am a programmer
Ignorance
Extra work
Yes!
Software engineering has come a long way since "top-down
modular design" was innovative
Testing is less effort than debugging
Learning to test your code will make you a more portable
programmer
You'll have more friends if you test your code
Yeah, right.
You write your programs once...and then write them all over again
when a change needs to be made (you're a real hard worker)
Meskimen's Law: There's never time to do it right, but there's
always time to do it over.
If you don't care about your code as you write it, you are a curse to your
employer
You won't care much about any of your code after a couple of weeks
anyway
Point: the best time to write tests is before and while you are
coding, not after (though after is better than nothing). You're
the best person to do that
You'll save time either at the beginning of the project (by not
writing tests) or at the end of the project (less debugging)
With regression tests, you will save time at the end of and
throughout the project
You will have more provably correct code sooner if you test
You will save oodles of time in the future with regression tests
You will never have real confidence in your code until you
have tests
Some weenies:
Larry Wall
and a cast of thousands, including
Tom Christiansen
Gurusamy Sarathy
Damian Conway
Simon Cozens
and more: consider CPAN
Ignorance
Someone Else's Problem (documentation team)
Looming deadline/short on time
Extra work
Documenting is for weenies!
(No, they can't. For the love of Pete, have a heart and
document your code)
You really are the only one who knows how this code works--if
you document it, then someone else will understand it too (and
you will be free!)
If you're really a poor writer, do the best you can and let the
doc team clean it up later
False laziness says that less work up front yields less total
work in the long run
explaining how your code works to others (this lasts as long as the
module and is the #1 benefit in my book)
rewriting code to get the API right
"We try to solve the problem by rushing through the design process
so that enough time will be left at the end of the project to uncover
errors that were made because we rushed through the design
process." --Glenford Myers
time spent documenting is time spent designing and is well spent
mental exertion
Larry Wall
and a cast of thousands
and more: consider CPAN
A Testing Example
Creating a module
Editing test.pl
Running tests
Fixing mistakes
Creating a Module
create a module:
Foo/Bar/Bar.pm
Foo/Bar/Changes
Foo/Bar/MANIFEST
Foo/Bar/Makefile.PL
Foo/Bar/test.pl
Goal: a module that can parse an Apache log file and give
summary information in the form of an object
#########################
# change 'tests => 1' to 'tests => last_test_to_print';
use Test;
BEGIN { plan tests => 1 };
use Log::Apache::Object;
ok(1); # If we made it this far, we're ok.
#########################
we write one or two tests for each "feature" we're about to add
use Test;
BEGIN { plan tests => 1 };
use Log::Apache::Object;
ok(1); # If we made it this far, we're ok.
## first test
my $obj;
ok( $obj = new Log::Apache::Object );
% perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for Log::Apache::Object
% make
cp Object.pm blib/lib/Log/Apache/Object.pm
Manifying blib/man3/Log::Apache::Object.3pm
% make test
PERL_DL_NONLAZY=1 /usr/bin/perl -Iblib/arch -Iblib/lib \
-I/usr/local/lib/perl5/5.6.1/i386-freebsd \
-I/usr/local/lib/perl5/5.6.1 test.pl
1..1
ok 1
Can't locate object method "new" via package \
"Log::Apache::Object" (perhaps you forgot to load \
"Log::Apache::Object"?) at test.pl line 7.
*** Error code 255
Stop in /usr/home/scottw/testing/Log/Apache/Object.
sub new {
my $self = { };
my $proto = shift;
my $class = ref($proto) || $proto;
bless $self, $class;
return $self;
}
% make test
cp Object.pm blib/lib/Log/Apache/Object.pm
PERL_DL_NONLAZY=1 /usr/bin/perl -Iblib/arch -Iblib/lib test.pl
1..2
ok 1
ok 2
Success!
Writing Code
Future Improvements
Documenting: SYNOPSIS
=head1 SYNOPSIS
use Log::Apache::Object;
my $obj =
new Log::Apache::Object(file => '/www/logs/access_log');
$obj->parse();
print "I have had " . $obj->count(status => 404) .
" errors since the log was rotated\n";
Documenting: DESCRIPTION
=head1 DESCRIPTION
B<Log::Apache::Object> is an OO class for parsing Apache log
files. The following methods are available:
=over 4
=item B<new>
Creates a new B<Log::Apache::Object>. Valid arguments are
key/value pairs:
file => "filename.log"
=item B<init>
Initializes the object; this is called when the object is
instantiated, but may be called any time thereafter to
re-initialize the object (e.g., with new data). Takes the same
arguments as B<new>.
Documenting: BUGS
=head1 BUGS
=over 4
=item *
Only parses Apache combined log format
=item *
Stores the entire file in memory
=item *
Does not cache data outside of the object (re-parse each time)
=back
Admission of Guilt
## create a file
open TMP, ">.tmp_$$" or die "$!\n";
print TMP <<'_FILE_';
10.1.1.11 - - [24/Mar/2003:08:25:10 -0700] "GET /nonexistent HTTP/1.1" \
404 300 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; (R1 1.3))"
10.1.1.11 - - [24/Mar/2003:08:25:15 -0700] "GET /nonexistentential HTTP/1.1" \
404 300 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; (R1 1.3))"
10.1.1.11 - - [24/Mar/2003:08:25:24 -0700] "GET /foo/bar.pl HTTP/1.1" \
200 9328 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; (R1 1.3))"
_FILE_
close TMP;
## now further tests
undef $obj;
ok( $obj = new Log::Apache::Object(file => ".tmp_$$") );
ok( $obj->parse() );
ok( $obj->count, 3 );
ok( $obj->count('status'), 2 ); ## two 404's and one 200
ok( $obj->count('status' => '404'), 2 );
ok( $obj->count('host'
=> '10.1.1.11'), 3 );
## clean up
END { unlink ".tmp_$$" }
...usually.
our %agg
= ();
our @lines = ();
sub new {
my $self = { };
my $proto = shift;
my $class = ref($proto) || $proto;
bless $self, $class;
$self->init(@_);
return $self;
}
sub init {
my $self = shift;
my %args = @_;
$self->file($args{'file'});
}
sub file {
my $self = shift;
my $file = shift;
return $self->{'_file'} = ( defined $file
? $file
: $self->{'_file'} );
}
$agg{user}->{$data{user}}++;
$agg{protocol}->{$data{protocol}}++;
$agg{referer}->{$data{referer}}++;
Future improvements:
Conclusion
Practical Take-away
Philosophical Take-Away
You'll have more friends if you test and document your code