Beruflich Dokumente
Kultur Dokumente
Kent Cowgill
Testing Code
and Assuring Quality
•Learn how to write unit tests in perl
•Write tests for your code
•Ensuring your code is high quality
•Ensuring your tests fully exercise your code
•Writing functional tests for your code
•A practical example of creating a test suite
•How to save time and effort (be lazy!)
What is
testing?
Testing.
Software testing is the process used to help identify the
correctness, completeness, security, and quality of
developed computer software. Testing is a process of
technical investigation, performed on behalf of
stakeholders, that is intended to reveal quality-related
information about the product with respect to the context
in which it is intended to operate. This includes, but is not
limited to, the process of executing a program or
application with the intent of finding errors. Quality is not
an absolute; it is value to some person. With that in mind,
testing can never completely establish the correctness of
arbitrary computer software; testing furnishes a criticism
or comparison that compares the state and behavior of
the product against a specification.
-- excerpted from http://en.wikipedia.org/wiki/Software_testing
Testing..
In software engineering, a test case is a set of conditions
or variables under which a tester will determine if a
requirement upon an application is partially or fully
satisfied.
OUTPUT:
ok 1 - $num equals 30
OUTPUT:
OUTPUT:
OUTPUT:
OUTPUT:
OUTPUT:
# in test.t at line 10
# got: 'this'
# expected: 'that'
Actual URL: http://pub.langworth.com/perl_test_refcard.pdf
Introducing Prove
PROVE(1) User Contributed Perl Documentation PROVE(1)
NAME
prove -- A command-line tool for running tests
OPTIONS
-d, --debug Includes extra debugging information
-h, --help Display this help
-H, --man Longer manpage for prove
-I Add libraries to @INC, as Perl's -I
-l, --lib Add lib to the path for your tests
-r, --recurse Recursively descend into directories
-s, --shuffle Run the tests in a random order
--timer Print elapsed time after each test file
-v, --verbose Display standard output of test scripts
while running
...
Output:
$ mv testmore.pl testmore.t
$ prove
./testmore....ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.02 cusr + 0.01
csys = 0.03 CPU)
$ prove -v
./testmore....ok 1 - this should equal thistoo
ok 2 - this should be thistoo (is)
ok 3 - this should NOT be that (isnt)
1..3
ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.02 cusr + 0.01
csys = 0.03 CPU)
How Many Tests?
#!/usr/bin/perl
use strict;
use warnings;
use strict;
use warnings;
I mean,
REALLY?
How do you
know?
Can you
prove it?
My
problem:
ZFML*
* Name changed to protect the innocent
(btw, what
the heck
is ZFML?)
ZFML is a custom
template system
ZFML is a
mish-mash of HTML
and Perl
ZFML only exists at
AcmeCorp.com*
my ($p) = @_;
$p->var->{'ONLOAD'} .= q(agentDOMCheck(););
$p->var->{'SCRIPT'} .= q(<script src="form_functions.js"></script>);
-->
my ($p) = @_;
-->
ZFML
<html> That looks
<head><title></title></head>
<body> like HTML
</body>
</html>
$p->var->{'ONLOAD'} .= q(agentDOMCheck(););
$p->var->{'SCRIPT'} .= q(<script src="form_functions.js"></script>);
-->
my ($p) = @_;
-->
It only runs
under
mod_perl
:(
$ perl -c index.zfml
package myObj;
sub new {
my $class = shift;
my %args = @_;
my $self = {};
$self->{ name } = $args{ name } || 'default';
return bless $self, $class;
}
sub set_name {
my $self = shift;
$self->{ name } = shift;
}
sub get_name {
my $self = shift;
return $self->{ name };
}
1;
#!/usr/bin/perl
A Simple Class
use strict;
use warnings;
package myObj;
Constructor
(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)
sub new {
my $class = shift;
my %args = @_;
my $self = {};
$self->{ name } = $args{ name } || 'default';
return bless $self, $class;
}
sub set_name {
my $self = shift;
$self->{ name } = shift;
}
sub get_name {
my $self = shift;
return $self->{ name };
}
1;
#!/usr/bin/perl
A Simple Class
use strict;
use warnings;
package myObj;
Constructor
(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)
sub new {
my $class = shift;
my %args = @_;
my $self = {};
$self->{ name } = $args{ name } || 'default';
return bless $self, $class;
}
sub set_name {
my $self = shift; Mutator
(http://en.wikipedia.org/wiki/Mutator_method)
$self->{ name } = shift;
}
sub get_name {
my $self = shift;
return $self->{ name };
}
1;
#!/usr/bin/perl
A Simple Class
use strict;
use warnings;
package myObj;
Constructor
(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)
sub new {
my $class = shift;
my %args = @_;
my $self = {};
$self->{ name } = $args{ name } || 'default';
return bless $self, $class;
}
sub set_name {
my $self = shift; Mutator
(http://en.wikipedia.org/wiki/Mutator_method)
$self->{ name } = shift;
}
sub get_name {
my $self = shift;
return $self->{ name };
Accessor
(http://en.wikipedia.org/wiki/Accessor)
}
1;
Using A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
use myObj;
...
Using A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
Calling the
use myObj; Constructor
my $obj = myObj->new( name => 'My Object' );
...
Using A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
Calling the
use myObj; Constructor
my $obj = myObj->new( name => 'My Object' );
my $objName = $obj->get_name();
...
Calling the
Accessor
Using A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
Calling the
use myObj; Constructor
my $obj = myObj->new( name => 'My Object' );
my $objName = $obj->get_name();
use strict;
use warnings;
use Test::More
Testing A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
Make sure
use Test::More 'no_plan';
you can
BEGIN { use_ok( 'myObj' ); } "use" the
object
Testing A Simple Class
#!/usr/bin/perl
use strict;
use warnings; Make sure you
use Test::More 'no_plan'; can instantiate
BEGIN { use_ok( 'myObj' ); } the object (call
ok( my $obj1 = myObj->new( name => 'test1'
the constructor)
),
"can create a myObj specifying values" );
Testing A Simple Class
#!/usr/bin/perl
use strict;
use warnings;
use strict;
use warnings;
use strict;
use warnings;
use strict;
use warnings;
Specify the
use Test::More tests => 8;
number of tests
BEGIN { use_ok( 'myObj' ); }
we intend to run
ok( my $obj1 = myObj->new( name => 'test1' ),
"can create a myObj specifying values" );
isa_ok( $obj1, 'myObj' );
$ prove testindex.t
testindex....
# Failed test 'use index.zfml;'
# in testindex.t at line 8.
# Tried to use 'index.zfml'.
# Error: syntax error at (eval 3) line 2, near "use index."
# Looks like you failed 1 test of 1.
testindex....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
Failed 1/1 tests, 0.00% okay
Failed Test Stat Wstat Total Fail List of Failed
---------------------------------------------------------------------
testindex.t 1 256 1 1 1
Failed 1/1 test scripts. 1/1 subtests failed.
Files=1, Tests=1, 0 wallclock secs ( 0.03 cusr + 0.01 csys = 0.04
CPU)
Failed 1/1 test programs. 1/1 subtests failed.
Ensuring
your code is
high*
quality**
* for some values of high
** for some values of quality
Introducing
Perl::Critic and perlcritic
Perl::Critic(3) User Contributed Perl Documentation Perl::Critic(3)
NAME
Perl::Critic - Critique Perl source code for best-practices
SYNOPSIS
use Perl::Critic;
my $file = shift;
my $critic = Perl::Critic->new();
my @violations = $critic->critique($file);
print @violations;
DESCRIPTION
Perl::Critic is an extensible framework for creating and
applying coding standards to Perl source code. Essentially, it
is a static source code analysis engine. Perl::Critic is
distributed with a number of Perl::Critic::Policy modules that
attempt to enforce various coding guidelines. Most Policy
modules are based on Damian Conway's book Perl Best Practices.
Introducing
Perl::Critic and perlcritic
PERLCRITIC(1) User Contributed Perl Documentation PERLCRITIC(1)
NAME
"perlcritic" - Command-line interface to critique Perl source
SYNOPSIS
perlcritic [-12345 | -severity number] [-noprofile | -profile file]
[-top [ number ]] [-include pattern] [-exclude pattern]
[-theme expression] [-verbose number | format] [-list]
[-only | -noonly] [-force | -noforce] [-nocolor] [-Version]
[-help] [-man] [-quiet] [FILE | DIRECTORY | STDIN]
DESCRIPTION
"perlcritic" is a Perl source code analyzer. It is the
executable front-end to the Perl::Critic engine, which attempts
to identify awkward, hard to read, error-prone, or
unconventional constructs in your code. Most of the rules are
based on Damian Conway's book Perl Best Practices.
Don't worry, it's all
in perldoc.
Working with perlcritic
$ perlcritic -1 myObj.pm
RCS keywords $Id$ not found at line 1, column 1. See page 441 of PBP.
(Severity: 2)
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See
page 441 of PBP. (Severity: 2)
RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. See
page 441 of PBP. (Severity: 2)
No "VERSION" variable found at line 1, column 1. See page 404 of PBP.
(Severity: 2)
Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)
Subroutine does not end with "return" at line 16, column 1. See page 197 of
PBP. (Severity: 4)
Working with perlcritic
$ perlcritic -1 myObj.pm
RCS keywords $Id$ not found at line 1, column 1. See page 441 of PBP.
(Severity: 2)
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See
page 441 of PBP. (Severity: 2)
RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. See
page 441 of PBP. (Severity: 2)
No "VERSION" variable found at line 1, column 1. See page 404 of PBP.
(Severity: 2)
Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)
Subroutine does not end with "return" at line 16, column 1. See page 197 of
PBP. (Severity: 4)
Working with .perlcriticrc
$ cat .perlcriticrc
[-Miscellanea::RequireRcsKeywords]
[-Modules::RequireVersionVar]
Working with perlcritic
$ perlcritic -1 myObj.pm
Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)
Subroutine does not end with "return" at line 16, column 1. See page 197 of
PBP. (Severity: 4)
Working with perlcritic
$ perlcritic -1 myObj.pm
Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)
Subroutine does not end with "return" at line 16, column 1. See page 197 of
PBP. (Severity: 4)
Working with perlcritic
sub set_name {
my $self = shift;
$self->{ name } = shift;
return;
}
Working with perlcritic
Output:
$ prove -v testobj.t
testobject....1..8
# Failed test 'can set name'
# in testobject.t at line 17.
# Looks like you failed 1 test of 8.
ok 1 - use myObj;
ok 2 - can create a myObj specifying values
ok 3 - The object isa myObj
ok 4 - can create a myObj not specifying values
ok 5 - The object isa myObj
not ok 6 - can set name
ok 7 - can get name
ok 8 - obj1 seems deeply similar to obj2
Files=1, Tests=8, 0 wallclock secs ( 0.03 cusr + 0.01 csys
= 0.04 CPU)
Failed 1/1 test programs. 1/8 subtests failed.
Working with perlcritic
#!/usr/bin/perl
use strict;
use warnings;
NAME
perltidy - a perl script indenter and reformatter
SYNOPSIS
perltidy [ options ] file1 file2 file3 ...
(output goes to file1.tdy, file2.tdy, ...)
perltidy [ options ] file1 -o outfile
perltidy [ options ] file1 -st >outfile
perltidy [ options ] <infile >outfile
Working with perltidy
$ cat .perltidyrc
-l=78 # Max line width is 78 cols
-i=2 # Indent level is 2 cols
-ci=2 # Continuation indent is 2 cols
-lp # line up parenthesis
-vt=2 # Maximal vertical tightness
-vtc=1 # medium vertical something tightness
-cti=1 # No extra indentation for closing brackets
-pt=1 # Medium parenthesis tightness
-bt=1 # Medium brace tightness
-sbt=1 # Medium square bracket tightness
-bbt=1 # Medium block brace tightness
-nsfs # No space before semicolons
-nolq # Don't outdent long quoted strings
-wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |=
>>= ||= .= %= ^= x="
# Break before all operators
-nsak="my local our if elsif until unless while for foreach return switch case
given when"
-bar
-cab=3
-wrs="! ," # want right space after these tokens
-wls="!" # want left space after !
Screencast
demonstration
removed
for PDF
ZFML
$ perltidy index.zfml
9: @import url(/AcmeCorp/templates/gateway85styles.css);
^
found Array where operator expected
9: @import url(/AcmeCorp/templates/gateway85styles.css);
------- ^
found bareword where operator expected (previous token underlined)
9: @import url(/AcmeCorp/templates/gateway85styles.css);
---------^
found bareword where operator expected (previous token underlined)
NAME
Devel::Cover - Code coverage metrics for Perl
SYNOPSIS
perl -MDevel::Cover yourprog args
cover
perl -MDevel::Cover=-db,cover_db,-coverage,statement,time
yourprog args
cover -delete
HARNESS_PERL_SWITCHES=-MDevel::Cover make test
cover
huh?
Introducing
Devel::Cover
$ perl -MDevel::Cover testobj.t
1..8
ok 1 - use myObj;
... # some Devel::Cover output snipped
ok 2 - can create a myObj specifying values
ok 3 - The object isa myObj
ok 4 - can create a myObj not specifying values
ok 5 - The object isa myObj
ok 6 - can set name
ok 7 - can get name
ok 8 - obj1 seems deeply similar to obj2
Devel::Cover: Writing coverage database to /Users/kentcowgill/cover_db/runs/
1169095517.23575.48192
---------------------------- ------ ------ ------ ------ ------ ------ ------
File stmt bran cond sub pod time total
---------------------------- ------ ------ ------ ------ ------ ------ ------
myObj.pm 100.0 n/a 100.0 100.0 n/a 23.5 100.0
testobj.t 100.0 n/a n/a 100.0 n/a 76.5 100.0
Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0
---------------------------- ------ ------ ------ ------ ------ ------ ------
Introducing
cover
$ cover
Reading database from /Users/kentcowgill/cover_db
OPTIONS
...
---------------------------- ------
File stmt
---------------------------- ------
/usr/bin/prove 73.7
myObj.pm 100.0
Tweaking
prove
$ cover
Reading database from /Users/kentco
---------------------------- ------
File stmt
---------------------------- ------
/usr/bin/prove 73.7
myObj.pm 100.0
yeah :(
Tweaking
prove
$ PERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove prove -v testobj.t
testobj....1..8
ok 1 - use myObj;
ok 2 - can create a myObj specifying values
ok 3 - The object isa myObj
ok 4 - can create a myObj not specifying values
ok 5 - The object isa myObj
ok 6 - can set name
ok 7 - can get name
ok 8 - obj1 seems deeply similar to obj2
ok
All tests successful.
Files=1, Tests=8, 3 wallclock secs ( 3.18 cusr + 0.08 csys = 3.26 CPU)
Saving Some Typing
$ cat Makefile
OPENCMD = open
BROWSER = /Applications/Safari.app
clean:
cover -delete
test:
prove testobj.t
cover:
make clean
PERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove make test 2>&1
cover
make report
report:
$(OPENCMD) $(BROWSER) cover_db/coverage.html
Saving Some Typing
$ make cover
make clean
cover -delete
Deleting database /Users/kentcowgill/cover_db
PERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove make test 2>&1
prove testobj.t
testobj....1..8
testobj....ok
All tests successful.
Files=1, Tests=8, 7 wallclock secs ( 3.22 cusr + 0.09 csys = 3.31 CPU)
cover
Reading database from /Users/kentcowgill/cover_db
NAME
Test::ZFML - Custom Test:: module built specifically for parsing
ZFML.
DESCRIPTION
Long has it been lamented that AcmeCorp's implementation of ZFML (and
who knows what that really stands for) is unmaintainable, and more
importantly untestable.
No more.
use strict;
use warnings;
my $p = ZFML->new();
my $file = q[test.zfml];
SYNOPSIS
# Emulates Test::Simple
package Your::Module;
my $CLASS = __PACKAGE__;
sub ok ($;$) {
my $tb = $CLASS->builder;
return $tb->ok(@_);
}
1;
Test::Builder::Module
NAME
Test::Builder::Module - Base class for test
modules
SYNOPSIS
# Emulates Test::Simple
package Your::Module;
my $CLASS = __PACKAGE__;
1;
Test::ZFML
package Test::ZFML;
use strict;
use warnings;
# global regexes
my $includeparse
= qr/<!--\s+__(TEMPLATE[\sA-Z]*?)__\s*?\n(.*?)\n-->/s;
my $htmlparse
= qr/<!--\s+__([ A-Z_]+)__\s*\n(.*?)\n-->/s;
my $zfmlparse
= qr/(<!--\s+__(?:EVAL|INIT|POST) [^ ]+__\s*\n.*?\n-->)/s;
my $zfmlextract
= qr/<!--\s+__(EVAL|INIT|POST) ([^ ]+)__\s*\n(.*?)\n-->/s;
Test::ZFML
Icky
regexes
# global regexes
my $includeparse
= qr/<!--\s+__(TEMPLATE[\sA-Z]*?)__\s*?\n(.*?)\n-->/s;
my $htmlparse
= qr/<!--\s+__([ A-Z_]+)__\s*\n(.*?)\n-->/s;
my $zfmlparse
= qr/(<!--\s+__(?:EVAL|INIT|POST) [^ ]+__\s*\n.*?\n-->)/s;
my $zfmlextract
= qr/<!--\s+__(EVAL|INIT|POST) ([^ ]+)__\s*\n(.*?)\n-->/s;
Test::ZFML
sub load_ok {
my $desc;
( $file_to_test, $desc ) = @_;
_load_file( $file_to_test );
$zfml_filestate = LOADED;
my $tb = Test::ZFML->builder;
sub _load_file {
$file_to_test = shift;
_get_contents( \$file_contents, $file_to_test );
push @vars, grep { ! /^\$(ENV|inp)/ }
$file_contents =~ m/(\$[A-Z_]+)/g;
return;
}
Test::ZFML
sub _load_file {
$file_to_test = shift;
_get_contents( \$file_contents, $file_to_test );
push @vars, grep { ! /^\$(ENV|inp)/ }
$file_contents =~ m/(\$[A-Z_]+)/g;
return;
}
Just does
a slurp
Test::ZFML
sub parse_ok {
my( $file, $p, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
croak 'You must load the file first' if
$zfml_filestate != LOADED;
_parse_file( $p );
$zfml_filestate = PARSED;
my $tb = Test::ZFML->builder;
...
Test::ZFML Really
sub _parse_file { parse it
my( $p ) = @_;
...
...
Test::ZFML
for my $key( keys %includes ){
# process all the include files :)
my $tb = Test::Zfml->builder;
$tb->ok( _get_includes( $key, $includes{ $key },
$file_to_test ),
"Included $key file $includes{ $key }"
);
}
Chug
for my $key( keys %zfmlvars ){
$p->var->{$key} = $zfmlvars{$key};
}
my $tb = Test::ZFML->builder();
$tb->ok( -e $filepath,
"Inlude/Template file ($filepath) Exists" );
...
Test::ZFML
Process
sub _get_includes {
my( $name, $file, $fromfile ) = @_;
my $filepath = "$webroot/$file";
my $tb = Test::ZFML->builder();
$tb->ok( -e $filepath,
"Inlude/Template file ($filepath) Exists" );
...
...
Test::ZFML
open( my $tmp, '<', $filepath )
or die "can't open include file";
my @file = <$tmp>;
my $contents;
for my $line ( @file ){
$contents .= $line;
if( $line =~ m/\$([A-Z]+)\s/ ){
eval "\$testzfml::$1 = 'dummy content'";
}
if( $line =~ m/var->{'([A-Z_]+)'}/ ){
eval "\$testzfml::$1 = 'dummy content'";
}
}
my %includes = $contents =~ /$includeparse/g;
for my $key( keys %includes ){
_get_includes( $key, $includes{ $key }, $file );
}
close( $tmp );
}
...
Test::ZFML Evaluate,
open( my $tmp, '<', $filepath )
or die "can't open include file";
my @file = <$tmp>;
evaluate,
my $contents;
for my $line ( @file ){
evaluate
$contents .= $line;
if( $line =~ m/\$([A-Z]+)\s/ ){
eval "\$testzfml::$1 = 'dummy content'";
}
if( $line =~ m/var->{'([A-Z_]+)'}/ ){
eval "\$testzfml::$1 = 'dummy content'";
}
}
my %includes = $contents =~ /$includeparse/g;
for my $key( keys %includes ){
_get_includes( $key, $includes{ $key }, $file );
}
close( $tmp );
}
Test::ZFML
sub evaluate_ok {
my( $file, $p, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
croak 'You must parse the file first'
if $zfml_filestate != PARSED;
$zfml_filestate = EVALED;
}
}
Really
# loads everything into memory for testing
require $_ for @cov_files; ## no critic
evaluate
}
it
Test::ZFML
sub _evaluate_code {
my( $p, $eval_init, $name, $hunk, $file, $desc ) = @_;
$file =~ s/.*\/(.*)$/$1/;
my $subname = "$eval_init$name";
$hunk = _wrap_hunk( $hunk, $subname );
my $filename = "$file.$subname";
my $tb = Test::ZFML->builder;
}
eval "testzfml::$subname( \$p );";
die "eval failed - $@" if $@; for Code
Coverage
sub _wrap_hunk {
Test::ZFML
my( $hunk, $subname ) = @_;
...
sub _wrap_hunk {
Test::ZFML
my( $hunk, $subname ) = @_;
Heredocs
([A-Z]+) # next the heredoc token
\1; # close quoting delimiter
(.*?)\n # the heredoc
\2 # closing heredoc token
/= $^R|$3|;/gsx; # replace with quoting
}
...
Test::ZFML
...
my $chunk;
return $chunk;
}
Test::ZFML
...
my $chunk;
namespace
return $chunk;
}
Test::ZFML
sub critique_ok {
my( $file, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
for my $hunk ( keys %{$stuff} ) {
for my $evals ( @{ $stuff->{$hunk} } ) {
for my $var ( keys %{$evals} ) {
_critique_code( $hunk, $var, $evals->{$var}, $desc );
}
}
}
}
Test::ZFML
sub critique_ok {
my( $file, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
for my $hunk ( keys %{$stuff} ) {
for my $evals ( @{ $stuff->{$hunk} } ) {
for my $var ( keys %{$evals} ) {
_critique_code( $hunk, $var, $evals->{$var}, $desc );
}
}
}
}
Critique
it
Test::ZFML
sub _critique_code {
my( $eval_init, $name, $hunk, $desc ) = @_;
my $subname = "$eval_init$name";
my $problems = 0;
sub contains_ok {
my( $file, $p, $regex, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
$p->render();
my $tb = Test::ZFML->builder;
$tb->like( $file_contents, $regex, $desc );
}
Check Test::ZFML
its'
contents
sub contains_ok {
my( $file, $p, $regex, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
$p->render();
my $tb = Test::ZFML->builder;
$tb->like( $file_contents, $regex, $desc );
}
Test::ZFML
sub lacks_ok {
my( $file, $p, $regex, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
$p->render();
my $tb = Test::ZFML->builder;
$tb->unlike( $file_contents, $regex, $desc );
}
Test::ZFML
sub lacks_ok {
my( $file, $p, $regex, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
$p->render();
my $tb = Test::ZFML->builder;
$tb->unlike( $file_contents, $regex, $desc );
}
Make sure it
doesn't have
specific bits
Test::ZFML
sub html_ok {
my( $file, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
Test::HTML::Lint::html_ok( $file_contents, $desc );
return;
}
Test::ZFML
Check the
HTML
sub html_ok {
my( $file, $desc ) = @_;
croak 'wrong file' if $file ne $file_to_test;
Test::HTML::Lint::html_ok( $file_contents, $desc );
return;
}
Putting it all together
• Test::More
• prove
• Perl::Critic
• Devel::Cover
• Makefile
• Test::ZFML
The Test File
Screencast
demonstration
removed
for PDF
The Makefile
Kent-Cowgills-Computer(~/acmecorp)$ cat Makefile
OPENCMD = open
BROWSER = /Applications/Safari.app
clean:
cover -delete
test:
prove t/
verbose:
prove -v t/
shuffle:
prove -s t/
cover:
make clean
PERL5OPT=-MDevel::Cover=+ignore,prove,+ignore,lib.*\.pm,
+ignore,zfml\.t make test
cover
make report
report:
$(OPENCMD) $(BROWSER) cover_db/coverage.html
Running Tests
NAME
Test::WWW::Mechanize - Testing-specific WWW::Mechanize subclass
VERSION
Version 1.12
SYNOPSIS
Test::WWW::Mechanize is a subclass of WWW::Mechanize that
incorporates features for web application testing. For example:
$mech->get_ok( $page );
$mech->base_is( 'http://petdance.com/', 'Proper <BASE HREF>' );
$mech->title_is( "Invoice Status", "On the invoice page" );
$mech->content_contains( "Andy Lester", "My name somewhere" );
$mech->content_like( qr/(cpan|perl)\.org/, "Link: perl or CPAN" );
Planetary::DblClick_tag.pm
#!/usr/bin/perl
use strict;
use warnings;
...
Planetary::DblClick_tag.pm
#!/usr/bin/perl
use strict;
use warnings;
...
Planetary::DblClick_tag.pm
...
...
Planetary::DblClick_tag.pm
...
...
Planetary::DblClick_tag.pm
$ prove -v dblclick.t
dblclick....ok 1 - Check base URL
ok 2 - received greeting message
ok 3 - got site=ac in iframe src tag
ok 4 - got affiliate=ac in iframe src tag
ok 5 - got app=tango in iframe src tag
ok 6 - got size=160x600 in iframe src tag
ok 7 - got pp=1 in iframe src tag
ok 8 - got path=$ID in iframe src tag
ok 9 - got dcpc=606 in iframe src tag
ok 10 - got ge=2 in iframe src tag
ok 11 - got age=19 in iframe src tag
ok 12 - got widget=14 in iframe src tag
ok 13 - got wango=5 in iframe src tag
ok 14 - got state=30 in iframe src tag
ok 15 - got tango=3.8 in iframe src tag
ok 16 - got doodad=0 in iframe src tag
ok 17 - got gadget=0075 in iframe src tag
ok 18 - got mtfnpy=4 in iframe src tag
...
ok
All tests successful.
Files=1, Tests=38, 8 wallclock secs ( 0.27 cusr + 0.08 csys = 0.35 CPU)
How To
Get
Started
The VIM Plugin
The VIM Plugin
Screencast
demonstration
removed
for PDF
The VIM Plugin
The Perl Module
The Perl Module
Screencast
demonstration
removed
for PDF
The Perl Module
~fin~
•Wikipedia:
References
http://en.wikipedia.org/wiki/Software_testing
http://en.wikipedia.org/wiki/Test_Case
•Test modules:
http://search.cpan.org/dist/Test-Simple/
•ViM script:
http://www.vim.org/scripts/script.php?script_id=1985
•Test::StubGenerator:
http://search.cpan.org/dist/Test-StubGenerator/
•Cats:
http://icanhascheezburger.com/