testing code and assuring quality

228
Testing Code and Assuring Quality Learning to use Test::More, Perl::Critic, and Devel::Cover Kent Cowgill

Upload: kent-cowgill

Post on 01-Nov-2014

11.835 views

Category:

Technology


3 download

DESCRIPTION

Learning to use Test::More, Perl::Critic, and Devel::Cover

TRANSCRIPT

Page 1: Testing Code and Assuring Quality

Testing Codeand Assuring Quality

Learning to use Test::More,Perl::Critic, and Devel::Cover

Kent Cowgill

Page 2: Testing Code and Assuring Quality

Testing Codeand 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!)

Page 3: Testing Code and Assuring Quality

What is testing?

Page 4: Testing Code and Assuring Quality

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

Page 5: Testing Code and Assuring Quality

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.

It may take many test cases to determine that a requirement is fully satisfied. In order to fully test that all the requirements of an application are met, there must be at least one test case for each requirement unless a requirement has sub requirements.

Some methodologies recommend creating at least two test cases for each requirement. One of them should perform positive testing of requirement and other should perform negative testing.

-- excerpted from http://en.wikipedia.org/wiki/Test_Case

Page 6: Testing Code and Assuring Quality

Testing...

What characterizes a formal, written test case is that there is a known input and an expected output, which is worked out before the test is executed.

If the application is created without formal requirements, then test cases are written based on the accepted normal operation of programs of a similar class.

-- excerpted from http://en.wikipedia.org/wiki/Test_Case

Page 7: Testing Code and Assuring Quality
Page 8: Testing Code and Assuring Quality

How can I find out more information about

testing with Perl?

Page 9: Testing Code and Assuring Quality

(or anything else you talk about tonight, since you don't really cover

anything in great depth?)

(yeah, sorry about that)

Page 10: Testing Code and Assuring Quality

Google

Page 11: Testing Code and Assuring Quality
Page 12: Testing Code and Assuring Quality
Page 13: Testing Code and Assuring Quality

Websites

Page 14: Testing Code and Assuring Quality
Page 15: Testing Code and Assuring Quality

CPAN1

Page 16: Testing Code and Assuring Quality
Page 17: Testing Code and Assuring Quality

CPAN2

Page 18: Testing Code and Assuring Quality

Screencast demonstration

removedfor PDF

Page 19: Testing Code and Assuring Quality

Books

Page 20: Testing Code and Assuring Quality
Page 21: Testing Code and Assuring Quality
Page 22: Testing Code and Assuring Quality

How to write unit

tests in Perl

Page 23: Testing Code and Assuring Quality

Unit tests emitTAP

Page 24: Testing Code and Assuring Quality

Test Anything Protocol(TAP)

• The Test Anything Protocol is a general purpose format for transmitting the result of test programs to a thing which interprets and takes action on those results.

Page 25: Testing Code and Assuring Quality

Test Anything Protocol(TAP)

1..Nok 1 Description # Directive# Diagnostic....ok 47 Descriptionok 48 Descriptionmore tests....

Page 26: Testing Code and Assuring Quality

Test Anything Protocol(TAP)

1..4ok 1 - Input file openednot ok 2 - First line of the input validok 3 - Read the rest of the filenot ok 4 - Summarized correctly # TODO

Page 27: Testing Code and Assuring Quality

Let's write some tests.

Page 28: Testing Code and Assuring Quality

Test::Simple• ok( <expression>, <description>);

ok( $num == 30, '$num equals 30' );

ok( $this =~ m/that/, 'this matches that' );

ok( do_it( $param ), 'sub do_it() returns true' );

OUTPUT:

ok 1 - $num equals 30

ok 2 - this matches that

ok 3 - sub do_it() returns true

Page 29: Testing Code and Assuring Quality

Test::Simple• ok( <expression>, <description>);

ok( $num == 30, '$num equals 30' );

ok( $this =~ m/that/, 'this matches that' );

ok( do_it( $param ), 'sub do_it() returns true' );

OUTPUT:

not ok 1 - $num equals 30

# Failed test '$num equals 30'

# in test.pl at line 10.

Page 30: Testing Code and Assuring Quality

Test::Simple• ok( <expression>, <description>);

ok( $num == 30, '$num equals 30' );

ok( $this =~ m/that/, 'this matches that' );

ok( do_it( $param ), 'sub do_it() returns true' );

OUTPUT:

not ok 2 - this matches that

# Failed test 'this matches that'

# in test.pl at line 11.

Page 31: Testing Code and Assuring Quality

Test::Simple• ok( <expression>, <description>);

ok( $num == 30, '$num equals 30' );

ok( $this =~ m/that/, 'this matches that' );

ok( do_it( $param ), 'sub do_it() returns true' );

OUTPUT:

not ok 3 - sub do_it() returns true

# Failed test 'sub do_it() returns true'

# in test.pl at line 13.

Page 32: Testing Code and Assuring Quality

Test::More• is( <got>, <expected>, <description>);

is( $this, $that, 'this is the same as that' );

Page 33: Testing Code and Assuring Quality

Test::More• is( <got>, <expected>, <description>);

is( $this, $that, 'this is the same as that' );

OUTPUT:

ok 1 - this is the same as that

Page 34: Testing Code and Assuring Quality

Test::More• is( <got>, <expected>, <description>);

is( $this, $that, 'this is the same as that' );

OUTPUT:

not ok 1 - this is the same as that

# Failed test 'this is equal to that'

# in test.t at line 10

# got: 'this'

# expected: 'that'

Page 35: Testing Code and Assuring Quality

Actual URL: http://pub.langworth.com/perl_test_refcard.pdf

Page 36: Testing Code and Assuring Quality

Introducing ProvePROVE(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

...

Page 37: Testing Code and Assuring Quality

$ 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 thistoook 2 - this should be thistoo (is)ok 3 - this should NOT be that (isnt)1..3okAll tests successful.Files=1, Tests=3, 0 wallclock secs ( 0.02 cusr + 0.01 csys = 0.03 CPU)

Output:

Page 38: Testing Code and Assuring Quality

#!/usr/bin/perl

use strict;use warnings;

use Test::More tests => 3;

# set some testing variablesmy $this = "this";my $thistoo = "this";my $that = "that";

# now for the testsok( $this eq $thistoo, "this should equal thistoo" );is( $this, $thistoo, "this should be thistoo (is)" );isnt( $this, $that, "this should NOT be that (isnt)" );

How Many Tests?

Page 39: Testing Code and Assuring Quality

$ prove -v./testmore....1..3ok 1 - this should equal thistoook 2 - this should be thistoo (is)ok 3 - this should NOT be that (isnt)okAll tests successful.Files=1, Tests=3, 0 wallclock secs ( 0.02 cusr + 0.01 csys = 0.03 CPU)

How Many Tests?

Page 40: Testing Code and Assuring Quality

#!/usr/bin/perl

use strict;use warnings;

use Test::More tests => 4;

# set some testing variablesmy $this = "this";my $thistoo = "this";my $that = "that";

# now for the testsok( $this eq $thistoo, "this should equal thistoo" );is( $this, $thistoo, "this should be thistoo (is)" );isnt( $this, $that, "this should NOT be that (isnt)" );

How Many Tests?

Page 41: Testing Code and Assuring Quality

$ prove -vtestmore....1..4ok 1 - this equals thistoook 2 - another way to see if this and thistoo are equal# Looks like you planned 4 tests but only ran 3.ok 3 - a way to see if this and that are not equaldubious Test returned status 255 (wstat 65280, 0xff00)DIED. FAILED test 4 Failed 1/4 tests, 75.00% okayFailed Test Stat Wstat Total Fail List of Failed-------------------------------------------------------------testmore.t 255 65280 4 2 4Failed 1/1 test scripts. 1/4 subtests failed.Files=1, Tests=4, 0 wallclock secs ( 0.02 cusr + 0.01 csys = 0.03 CPU)Failed 1/1 test programs. 1/4 subtests failed.

How Many Tests?

Page 42: Testing Code and Assuring Quality

Why prove, anyhow?

Page 43: Testing Code and Assuring Quality
Page 44: Testing Code and Assuring Quality

-l, --lib Add lib to the path for your tests

Page 45: Testing Code and Assuring Quality

-r, --recurse Recursively descend into directories

-l, --lib Add lib to the path for your tests

Page 46: Testing Code and Assuring Quality

-r, --recurse Recursively descend into directories

-s, --shuffle Run the tests in a random order

-l, --lib Add lib to the path for your tests

Page 47: Testing Code and Assuring Quality

That's great

Page 48: Testing Code and Assuring Quality

but...

Page 49: Testing Code and Assuring Quality

how does that help me? :-/

Page 50: Testing Code and Assuring Quality

perl -c

Page 51: Testing Code and Assuring Quality

Your problem:

Page 52: Testing Code and Assuring Quality

Your code compiles, but does it do the

right thing?

Page 53: Testing Code and Assuring Quality

Does it?

I mean, REALLY?

Page 54: Testing Code and Assuring Quality

How do you know?

Page 55: Testing Code and Assuring Quality

Can youprove it?

Page 56: Testing Code and Assuring Quality

My problem:ZFML*

* Name changed to protect the innocent

Page 57: Testing Code and Assuring Quality

(btw, whatthe heckis ZFML?)

Page 58: Testing Code and Assuring Quality

ZFML is a custom template system

Page 59: Testing Code and Assuring Quality

ZFML is amish-mash of HTML

and Perl

Page 60: Testing Code and Assuring Quality

ZFML only exists at AcmeCorp.com*

* Name changed to protect the innocent

Page 61: Testing Code and Assuring Quality

I don't think you'd want it to exist anywhere else.

Page 62: Testing Code and Assuring Quality

SRSLY

Page 63: Testing Code and Assuring Quality

<html> <head><title></title></head> <body> </body></html>

<!-- __INIT SETUP__

my ($p) = @_;

$p->var->{'ONLOAD'} .= q(agentDOMCheck(););$p->var->{'SCRIPT'} .= q(<script src="form_functions.js"></script>);

-->

<!-- __EVAL COPYRIGHT_YEAR__

my ($p) = @_;

$p->var->{'COPYRIGHT_YEAR'} = 1900 + (localtime)[5];

-->

ZFMLThat looks like HTML

Page 64: Testing Code and Assuring Quality

<html> <head><title></title></head> <body> </body></html>

<!-- __INIT SETUP__

my ($p) = @_;

$p->var->{'ONLOAD'} .= q(agentDOMCheck(););$p->var->{'SCRIPT'} .= q(<script src="form_functions.js"></script>);

-->

<!-- __EVAL COPYRIGHT_YEAR__

my ($p) = @_;

$p->var->{'COPYRIGHT_YEAR'} = 1900 + (localtime)[5];

-->

ZFMLThat looks like HTML

WTF?!?

Page 65: Testing Code and Assuring Quality

It only runs under

mod_perl

Page 66: Testing Code and Assuring Quality

:(

Page 67: Testing Code and Assuring Quality

$ perl -c index.zfml

Bareword found where operator expected at index.zfml line 5, near "<meta http-equiv="content-type" content="text/html"(Might be a runaway multi-line // string starting on line4) (Missing operator before html?)String found where operator expected at index.zfml line 6, near "<meta name="" (Might be a runaway multi-line "" string starting on line 5) (Missing semicolon on previous line?)Bareword found where operator expected at index.zfml line 6, near "<meta name="description" (Missing operator before description?)String found where operator expected at index.zfml line 6, near "description" content=""Bareword found where operator expected at index.zfml line 6, near "" content="Find" (Missing operator before Find?)Bareword found where operator expected at index.zfml line 7, near "<meta NAME="keywords" (Might be a runaway multi-line "" string starting on line 6) (Missing operator before keywords?)String found where operator expected at index.zfml line 7, near "keywords" CONTENT=""Bareword found where operator expected at index.zfml line 7, near "" CONTENT="AcmeCorp" (Missing operator before AcmeCorp?)Bareword found where operator expected at index.zfml line 7, near "time jobs" (Do you need to predeclare time?)String found where operator expected at index.zfml line 8, near "<style type="" (Might be a runaway multi-line "" string starting on line 7) (Missing semicolon on previous line?)Bareword found where operator expected at index.zfml line 8, near "<style type="text" (Missing operator before text?)String found where operator expected at index.zfml line 28, near "<div id="" (Might be a runaway multi-line "" string starting on line 8) (Missing semicolon on previous line?)Bareword found where operator expected at index.zfml line 28, near "<div id="pageContainer" (Missing operator before pageContainer?)

Page 68: Testing Code and Assuring Quality
Page 69: Testing Code and Assuring Quality

Write tests for

your code

Page 70: Testing Code and Assuring Quality

A Simple Class#!/usr/bin/perl

use strict;use warnings;

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;

Page 71: Testing Code and Assuring Quality

A Simple Class

Constructor(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)

#!/usr/bin/perl

use strict;use warnings;

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;

Page 72: Testing Code and Assuring Quality

A Simple Class

Constructor(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)

Mutator(http://en.wikipedia.org/wiki/Mutator_method)

#!/usr/bin/perl

use strict;use warnings;

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;

Page 73: Testing Code and Assuring Quality

A Simple Class

Constructor(http://en.wikipedia.org/wiki/Constructor_%28computer_science%29)

Accessor(http://en.wikipedia.org/wiki/Accessor)

Mutator(http://en.wikipedia.org/wiki/Mutator_method)

#!/usr/bin/perl

use strict;use warnings;

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;

Page 74: Testing Code and Assuring Quality

Using A Simple Class#!/usr/bin/perl

use strict;use warnings;

use myObj;

...

Page 75: Testing Code and Assuring Quality

Using A Simple Class

Calling theConstructor

#!/usr/bin/perl

use strict;use warnings;

use myObj;

my $obj = myObj->new( name => 'My Object' );

...

Page 76: Testing Code and Assuring Quality

Using A Simple Class

Calling theConstructor

Calling theAccessor

#!/usr/bin/perl

use strict;use warnings;

use myObj;

my $obj = myObj->new( name => 'My Object' );

my $objName = $obj->get_name();

...

Page 77: Testing Code and Assuring Quality

Using A Simple Class#!/usr/bin/perl

use strict;use warnings;

use myObj;

my $obj = myObj->new( name => 'My Object' );

my $objName = $obj->get_name();

my $new_name = 'Your Object' );

$obj->set_name( $new_name );

Calling theConstructor

Calling theAccessor

Calling theMutator

Page 78: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More

Page 79: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

It's fine to start out without a testing plan

(number of tests to run)

Page 80: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

Make sure you can "use" the

object

Page 81: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );

Make sure you can instantiate the object (call

the constructor)

Page 82: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

Make sure your instantiated

object "isa" type of object you

created

Page 83: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );

Instantiate another object

Page 84: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' ); Make sure the

new object "isa" "myObj" object

Page 85: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' );

ok( $obj2->set_name( 'test1' ), "can set name" );

Test using the mutator of the

name property of the object

Page 86: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' );

ok( $obj2->set_name( 'test1' ), "can set name" );ok( 'test1' eq $obj2->get_name(), "can get name" );

Make sure the accessor returns the value we just

set

Page 87: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More 'no_plan';

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' );

ok( $obj2->set_name( 'test1' ), "can set name" );ok( 'test1' eq $obj2->get_name(), "can get name" );

is_deeply( $obj1, $obj2, "obj1 seems deeply similar to obj2" );

Perform a "deep" comparison of the two objects

(created in different ways)

Page 88: Testing Code and Assuring Quality

Testing A Simple Class#!/usr/bin/perl

use strict;use warnings;

use Test::More tests => 8;

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' );

ok( $obj2->set_name( 'test1' ), "can set name" );ok( 'test1' eq $obj2->get_name(), "can get name" );

is_deeply( $obj1, $obj2, "obj1 seems deeply similar to obj2" );

Specify the number of tests we intend to run

Page 89: Testing Code and Assuring Quality

$ prove -v testobj.ttestobj....1..8ok 1 - use myObj;ok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2okAll tests successful.Files=1, Tests=8, 0 wallclock secs ( 0.02 cusr + 0.01 csys = 0.03 CPU)

Output:

Testing A Simple Class

Page 90: Testing Code and Assuring Quality

That's great

Page 91: Testing Code and Assuring Quality

but...

Page 92: Testing Code and Assuring Quality

how does that help me? :-|

Page 93: Testing Code and Assuring Quality

$ cat testindex.t...BEGIN { use_ok( 'index.zfml' ) };...

$ 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% okayFailed Test Stat Wstat Total Fail List of Failed---------------------------------------------------------------------testindex.t 1 256 1 1 1Failed 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.

Testing Zfml

Page 94: Testing Code and Assuring Quality
Page 95: Testing Code and Assuring Quality

Ensuring your code is

high* quality**

Page 96: Testing Code and Assuring Quality

* for some values of high

Page 97: Testing Code and Assuring Quality

** for some values of quality

Page 98: Testing Code and Assuring Quality

IntroducingPerl::Critic and perlcriticPerl::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.

Page 99: Testing Code and Assuring Quality

IntroducingPerl::Critic and perlcriticPERLCRITIC(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.

Page 100: Testing Code and Assuring Quality

Don't worry, it's all in perldoc.

Page 101: Testing Code and Assuring Quality

$ 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

Page 102: Testing Code and Assuring Quality

$ 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

Page 103: Testing Code and Assuring Quality

$ cat .perlcriticrc [-Miscellanea::RequireRcsKeywords][-Modules::RequireVersionVar]

Working with .perlcriticrc

Page 104: Testing Code and Assuring Quality

$ 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

Page 105: Testing Code and Assuring Quality

$ 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

Page 106: Testing Code and Assuring Quality

Working with perlcritic

sub set_name { my $self = shift; $self->{ name } = shift; return;}

Page 107: Testing Code and Assuring Quality

$ prove -v testobj.ttestobject....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 valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjnot ok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2Files=1, Tests=8, 0 wallclock secs ( 0.03 cusr + 0.01 csys = 0.04 CPU)Failed 1/1 test programs. 1/8 subtests failed.

Output:

Working with perlcritic

Page 108: Testing Code and Assuring Quality

#!/usr/bin/perl

use strict;use warnings;

use Test::More tests => 8;

BEGIN { use_ok( 'myObj' ); }

ok( my $obj1 = myObj->new( name => 'test1' ), "can create a myObj specifying values" );isa_ok( $obj1, 'myObj' );

ok( my $obj2 = myObj->new(), "can create a myObj not specifying values" );isa_ok( $obj2, 'myObj' );

ok( ! $obj2->set_name( 'test1' ), "can set name" );ok( 'test1' eq $obj2->get_name(), "can get name" );

is_deeply( $obj1, $obj2, "obj1 seems deeply similar to obj2" );

The mutator shouldn't return

a value!

Working with perlcritic

Page 109: Testing Code and Assuring Quality

$ prove -v testobj.ttestobj....1..8ok 1 - use myObj;ok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2okAll tests successful.Files=1, Tests=8, 0 wallclock secs ( 0.02 cusr + 0.01 csys = 0.03 CPU)

Output:

Working with perlcritic

Page 110: Testing Code and Assuring Quality

Perl::Critic and Zfml$ perlcritic -1 index.zfmlCode not contained in explicit package at line 1, column 1. Violates encapsulation. (Severity: 4)Code before strictures are enabled at line 1, column 1. See page 429 of PBP. (Severity: 5)Code before warnings are enabled at line 1, column 1. See page 431 of PBP. (Severity: 4)Mixed high and low-precedence booleans at line 1, column 1. See page 70 of PBP. (Severity: 4)Useless interpolation of literal string at line 1, column 23. See page 51 of PBP. (Severity: 1)Useless interpolation of literal string at line 1, column 64. See page 51 of PBP. (Severity: 1)Useless interpolation of literal string at line 2, column 13. See page 51 of PBP. (Severity: 1)Hard tabs used at line 4, column 60. See page 20 of PBP. (Severity: 3)Code not contained in explicit package at line 5, column 54. Violates encapsulation. (Severity: 4)Mixed high and low-precedence booleans at line 5, column 54. See page 70 of PBP. (Severity: 4)Hard tabs used at line 5, column 72. See page 20 of PBP. (Severity: 3)Useless interpolation of literal string at line 5, column 72. See page 51 of PBP. (Severity: 1)Useless interpolation of literal string at line 6, column 26. See page 51 of PBP. (Severity: 1)Postfix control "for" used at line 6, column 164. See page 96 of PBP. (Severity: 1)Hard tabs used at line 6, column 259. See page 20 of PBP. (Severity: 3)Useless interpolation of literal string at line 6, column 259. See page 51 of PBP. (Severity: 1)

Useless interpolation of literal string at line 7, column 23. See page 51 of PBP. (Severity: 1)Postfix control "for" used at line 7, column 261. See page 96 of PBP. (Severity: 1)Postfix control "for" used at line 7, column 393. See page 96 of PBP. (Severity: 1)Postfix control "for" used at line 7, column 568. See page 96 of PBP. (Severity: 1)Postfix control "for" used at line 7, column 587. See page 96 of PBP. (Severity: 1)Hard tabs used at line 7, column 678. See page 20 of PBP. (Severity: 3)Useless interpolation of literal string at line 7, column 678. See page 51 of PBP. (Severity: 1)Hard tabs used at line 8, column 24. See page 20 of PBP. (Severity: 3)Useless interpolation of literal string at line 33, column 22. See page 51 of PBP. (Severity: 1)Mismatched operator at line 34, column 15. Numeric/string operators and operands should match. (Severity: 3)Useless interpolation of literal string at line 34, column 45. See page 51 of PBP. (Severity: 1)Useless interpolation of literal string at line 34, column 64. See page 51 of PBP. (Severity: 1)Mismatched operator at line 34, column 86. Numeric/string operators and operands should match. (Severity: 3)Useless interpolation of literal string at line 34, column 186. See page 51 of PBP. (Severity: 1)Hard tabs used at line 34, column 209. See page 20 of PBP. (Severity: 3)Useless interpolation of literal string at line 34, column 209. See page 51 of PBP. (Severity: 1)

Page 111: Testing Code and Assuring Quality
Page 112: Testing Code and Assuring Quality

$ perlcritic -1 myObj.pm Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)

Working with perlcritic

Page 113: Testing Code and Assuring Quality

$ perlcritic -1 myObj.pm Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)

Working with perlcritic

Page 114: Testing Code and Assuring Quality

PERLTIDY(1) User Contributed Perl Documentation PERLTIDY(1)

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

Page 115: Testing Code and Assuring Quality

$ 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 !

Working with perltidy

Page 116: Testing Code and Assuring Quality

Screencast demonstration

removedfor PDF

Page 117: Testing Code and Assuring Quality

ZFML

Page 118: Testing Code and Assuring Quality

$ perltidy index.zfml

There is no previous '?' to match a ':' on line 44: <title>AcmeCorp: Widgets, Gadgets and Doodads</title> ^

5: <meta http-equiv="content-type" content="text/html;charset=iso-8 ... -------------- ^ found bareword where operator expected (previous token underlined)

5: ... ent="text/html;charset=iso-8859-1" /> -^found > where term expected (previous token underlined)

7: <meta NAME="keywords" CONTENT="AcmeCorp, widgets, gadgets ... ---------- ^ found bareword where operator expected (previous token underlined)

9: @import url(/AcmeCorp/templates/gateway85styles.css); ^ found Array where operator expected

Missing ';' above?

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)

Missing ';' above?

There is no previous '?' to match a ':' on line 1414: max-height: 140px; /* to fix valid */ ^Missing ';' above?

There is no previous '?' to match a ':' on line 1515: padding: 12px; margin-top: 5px; border-top: 1px solid #e0e0e0; ^

There is no previous '?' to match a ':' on line 15

15: padding: 12px; margin-top: 5px; border-top: 1px solid #e0e0e0;

^

There is no previous '?' to match a ':' on line 1515: padding: 12px; margin-top: 5px; border-top: 1px solid #e0e0e0; ^

Page 119: Testing Code and Assuring Quality

$ perlcritic -1 myObj.pm myObj.pm source OK

Working with perlcritic

Page 120: Testing Code and Assuring Quality

That's great

Page 121: Testing Code and Assuring Quality

but...

Page 122: Testing Code and Assuring Quality

how does that help me? :-\

Page 123: Testing Code and Assuring Quality
Page 124: Testing Code and Assuring Quality

Ensuring your tests

fully exercise your code

Page 125: Testing Code and Assuring Quality

Introducing Devel::Cover

Devel::Cover(3) Perl Documentation Devel::Cover(3)

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

To test an uninstalled module:

cover -delete HARNESS_PERL_SWITCHES=-MDevel::Cover make test cover

Page 126: Testing Code and Assuring Quality

huh?

Page 127: Testing Code and Assuring Quality

Introducing Devel::Cover

$ perl -MDevel::Cover testobj.t1..8ok 1 - use myObj;... # some Devel::Cover output snippedok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2Devel::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.0testobj.t 100.0 n/a n/a 100.0 n/a 76.5 100.0Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0---------------------------- ------ ------ ------ ------ ------ ------ ------

Page 128: Testing Code and Assuring Quality

Introducingcover

$ coverReading database from /Users/kentcowgill/cover_db

---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------myObj.pm 100.0 n/a 100.0 100.0 n/a 23.5 100.0testobj.t 100.0 n/a n/a 100.0 n/a 76.5 100.0Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0---------------------------- ------ ------ ------ ------ ------ ------ ------

Writing HTML output to /Users/kentcowgill/cover_db/coverage.html ...done.

Page 129: Testing Code and Assuring Quality

Introducingcover

$ coverReading database from /Users/kentcowgill/cover_db

---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------myObj.pm 100.0 n/a 100.0 100.0 n/a 23.5 100.0testobj.t 100.0 n/a n/a 100.0 n/a 76.5 100.0Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0---------------------------- ------ ------ ------ ------ ------ ------ ------

Writing HTML output to /Users/kentcowgill/cover_db/coverage.html ...done.

Page 130: Testing Code and Assuring Quality
Page 131: Testing Code and Assuring Quality

html?:-D

Page 132: Testing Code and Assuring Quality
Page 133: Testing Code and Assuring Quality
Page 134: Testing Code and Assuring Quality
Page 135: Testing Code and Assuring Quality
Page 136: Testing Code and Assuring Quality
Page 137: Testing Code and Assuring Quality
Page 138: Testing Code and Assuring Quality

TweakingDevel::Cover

Devel::Cover(3) Perl Documentation Devel::Cover(3)

OPTIONS

...

-ignore RE - Set REs of files to ignore +ignore RE - Append to REs of files to ignore.

Page 139: Testing Code and Assuring Quality

TweakingDevel::Cover

$ perl -MDevel::Cover=+ignore,.*\.t testobj.t1..8ok 1 - use myObj;... # Devel::Cover output snippedIgnoring packages matching: /Devel/Cover[./] .*.t... # Devel::Cover output snippedok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2Devel::Cover: Writing coverage database to /Users/kentcowgill/cover_db/runs/1169096938.23619.10353---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------myObj.pm 100.0 n/a 100.0 100.0 n/a 100.0 100.0Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0---------------------------- ------ ------ ------ ------ ------ ------ ------

Page 140: Testing Code and Assuring Quality

what happened to prove?

Page 141: Testing Code and Assuring Quality

Tweakingprove

$ prove -MDevel::Cover=+ignore,.*\.t testobj.t

Page 142: Testing Code and Assuring Quality

Tweakingprove

Unknown option: MUnknown option: eUnknown option: eUnknown option: :Unknown option: :Unknown option: CUnknown option: oUnknown option: eUnknown option: =Unknown option: +Unknown option: iUnknown option: gUnknown option: nUnknown option: oUnknown option: eUnknown option: ,Unknown option: .Unknown option: *Unknown option: .

$ prove -MDevel::Cover=+ignore,.*\.t testobj.t

Page 143: Testing Code and Assuring Quality

ouch!

Page 144: Testing Code and Assuring Quality

Tweakingprove

$ PERL5OPT=-MDevel::Cover=+ignore,.*\.t prove -v testobj.ttestobj....1..8ok 1 - use myObj;ok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2okAll tests successful.Files=1, Tests=8, 3 wallclock secs ( 3.18 cusr + 0.08 csys = 3.26 CPU)

Page 145: Testing Code and Assuring Quality

Tweakingprove

$ coverReading database from /Users/kentcowgill/cover_db

---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------/usr/bin/prove 73.7 43.8 0.0 46.7 n/a 98.0 61.1myObj.pm 100.0 n/a 100.0 100.0 n/a 2.0 100.0Total 78.0 43.8 40.0 60.0 n/a 100.0 66.9---------------------------- ------ ------ ------ ------ ------ ------ ------

Writing HTML output to /Users/kentcowgill/cover_db/coverage.html ...done.

Page 146: Testing Code and Assuring Quality

uh, was that 'prove' in there?

Page 147: Testing Code and Assuring Quality

Tweakingprove

$ coverReading database from /Users/kentco

---------------------------- ------ File stmt---------------------------- ------/usr/bin/prove 73.7myObj.pm 100.0

Page 148: Testing Code and Assuring Quality

Tweakingprove

$ coverReading database from /Users/kentco

---------------------------- ------ File stmt---------------------------- ------/usr/bin/prove 73.7myObj.pm 100.0

Page 149: Testing Code and Assuring Quality

yeah :(

Page 150: Testing Code and Assuring Quality

Tweakingprove

$ PERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove prove -v testobj.ttestobj....1..8ok 1 - use myObj;ok 2 - can create a myObj specifying valuesok 3 - The object isa myObjok 4 - can create a myObj not specifying valuesok 5 - The object isa myObjok 6 - can set nameok 7 - can get nameok 8 - obj1 seems deeply similar to obj2okAll tests successful.Files=1, Tests=8, 3 wallclock secs ( 3.18 cusr + 0.08 csys = 3.26 CPU)

Page 151: Testing Code and Assuring Quality

Saving Some Typing

$ cat MakefileOPENCMD = openBROWSER = /Applications/Safari.appclean: cover -deletetest: prove testobj.tcover: make clean PERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove make test 2>&1 cover make reportreport: $(OPENCMD) $(BROWSER) cover_db/coverage.html

Page 152: Testing Code and Assuring Quality

Saving Some Typing

$ make covermake cleancover -deleteDeleting database /Users/kentcowgill/cover_dbPERL5OPT=-MDevel::Cover=+ignore,.*\.t,+ignore,prove make test 2>&1prove testobj.ttestobj....1..8testobj....ok All tests successful.Files=1, Tests=8, 7 wallclock secs ( 3.22 cusr + 0.09 csys = 3.31 CPU)coverReading database from /Users/kentcowgill/cover_db

---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------myObj.pm 100.0 n/a 100.0 100.0 n/a 100.0 100.0Total 100.0 n/a 100.0 100.0 n/a 100.0 100.0---------------------------- ------ ------ ------ ------ ------ ------ ------

Writing HTML output to /Users/kentcowgill/cover_db/coverage.html ... done.make reportopen /Applications/Safari.app cover_db/coverage.html

Page 153: Testing Code and Assuring Quality
Page 154: Testing Code and Assuring Quality

100% yay!8-D

Page 155: Testing Code and Assuring Quality

IntroducingTest::ZFML

Test::Zfml(3) User Contributed Perl Documentation Test::Zfml(3)

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.

Test::ZFML attempts to make the unparseable parseable, the unmaintain- able maintainable, and the untestable testable. It does this by implementing it's own mini ZFML parser and places chunks of ZFML inside their own package, surrounded by their own subroutines which have defined inputs and testable outputs.

Page 156: Testing Code and Assuring Quality

UsingTest::ZFML

#!/usr/bin/perl

use strict;use warnings;

use Test::More qw/no_plan/;use Test::ZFML;use ZFML;

my $p = ZFML->new();

my $file = q[test.zfml];

load_ok( $file, "Loaded ZFML file $file" ); parse_ok( $file, "Parsed ZFML file $file" );evaluate_ok( $file, "Evaluated ZFML file $file" );critique_ok( $file, "Critiqued ZFML file $file" );

Page 157: Testing Code and Assuring Quality

That's great

Page 158: Testing Code and Assuring Quality

but...

Page 159: Testing Code and Assuring Quality

How about a demo?

Page 160: Testing Code and Assuring Quality

Screencast demonstration

removedfor PDF

Page 161: Testing Code and Assuring Quality

How'd you do that?

Page 162: Testing Code and Assuring Quality

Test::Builder::ModuleNAME Test::Builder::Module - Base class for test modules

SYNOPSIS # Emulates Test::Simple package Your::Module;

my $CLASS = __PACKAGE__;

use base 'Test::Builder::Module'; @EXPORT = qw(ok);

sub ok ($;$) { my $tb = $CLASS->builder; return $tb->ok(@_); }

1;

Page 163: Testing Code and Assuring Quality

Test::Builder::ModuleNAME Test::Builder::Module - Base class for test modules

SYNOPSIS # Emulates Test::Simple package Your::Module;

my $CLASS = __PACKAGE__;

use base 'Test::Builder::Module'; @EXPORT = qw(ok);

sub ok ($;$) { my $tb = $CLASS->builder; return $tb->ok(@_); }

1;

Start Here

Page 164: Testing Code and Assuring Quality

package Test::ZFML;

use strict;use warnings;

use Perl::Critic qw/critique/;use Test::HTML::Lint ();use Carp;

use lib '/Users/kentcowgill/acmecorp/lib';use ZFML;

use vars qw/$VERSION @ISA @EXPORT %EXPORT_TAGS $TODO/;

use base q/Test::Builder::Module/;

@EXPORT = qw/load_ok parse_ok evaluate_ok critique_ok replace_ok contains_ok lacks_ok html_ok/;

Test::ZFML

Page 165: Testing Code and Assuring Quality

package Test::ZFML;

use strict;use warnings;

use Perl::Critic qw/critique/;use Test::HTML::Lint ();use Carp;

use lib '/Users/kentcowgill/acmecorp/lib';use ZFML;

use vars qw/$VERSION @ISA @EXPORT %EXPORT_TAGS $TODO/;

use base q/Test::Builder::Module/;

@EXPORT = qw/load_ok parse_ok evaluate_ok critique_ok replace_ok contains_ok lacks_ok html_ok/;

Standard stuff

Test::ZFML

Page 166: Testing Code and Assuring Quality

# global regexesmy $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

Page 167: Testing Code and Assuring Quality

# global regexesmy $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;

Icky regexes

Test::ZFML

Page 168: Testing Code and Assuring Quality

sub load_ok { my $desc; ( $file_to_test, $desc ) = @_; _load_file( $file_to_test ); $zfml_filestate = LOADED; my $tb = Test::ZFML->builder;

# minimal (testable) sanity check, ensures that # $file_contents has contents $tb->ok( $file_contents, $desc );}

Test::ZFML

Page 169: Testing Code and Assuring Quality

sub load_ok { my $desc; ( $file_to_test, $desc ) = @_; _load_file( $file_to_test ); $zfml_filestate = LOADED; my $tb = Test::ZFML->builder;

# minimal (testable) sanity check, ensures that # $file_contents has contents $tb->ok( $file_contents, $desc );}

Load the file

Test::ZFML

Page 170: Testing Code and Assuring Quality

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

Page 171: Testing Code and Assuring Quality

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

Page 172: Testing Code and Assuring Quality

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;

# minimal (testable) sanity check, ensures that # $stuff got stuffed $tb->ok( $stuff, $desc );}

Test::ZFML

Page 173: Testing Code and Assuring Quality

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;

# minimal (testable) sanity check, ensures that # $stuff got stuffed $tb->ok( $stuff, $desc );} Parse the

file

Test::ZFML

Page 174: Testing Code and Assuring Quality

sub _parse_file { my( $p ) = @_;

# grab the executable hunks of perl code my @zfml = $file_contents =~ /$zfmlparse/g; $file_contents =~ s/$zfmlparse//g;

# grab the hunks that are responsible for templates my %includes = $file_contents =~ /$includeparse/g; $file_contents =~ s/$includeparse//g;

# finally, grab the hunks that get turned into HTML my %zfmlvars = $file_contents =~ /$htmlparse/g; $file_contents =~ s/$htmlparse//g;

...

Test::ZFML

Page 175: Testing Code and Assuring Quality

sub _parse_file { my( $p ) = @_;

# grab the executable hunks of perl code my @zfml = $file_contents =~ /$zfmlparse/g; $file_contents =~ s/$zfmlparse//g;

# grab the hunks that are responsible for templates my %includes = $file_contents =~ /$includeparse/g; $file_contents =~ s/$includeparse//g;

# finally, grab the hunks that get turned into HTML my %zfmlvars = $file_contents =~ /$htmlparse/g; $file_contents =~ s/$htmlparse//g;

...

Really parse it

Test::ZFML

Page 176: Testing Code and Assuring Quality

...

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 }" ); }

for my $key( keys %zfmlvars ){ $p->var->{$key} = $zfmlvars{$key}; }

for my $zfml( @zfml ){ if( $zfml =~ m/$zfmlextract/s ) { push @{ $stuff->{$1} }, { $2 => $3 }; } }} # end

Test::ZFML

Page 177: Testing Code and Assuring Quality

...

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 }" ); }

for my $key( keys %zfmlvars ){ $p->var->{$key} = $zfmlvars{$key}; }

for my $zfml( @zfml ){ if( $zfml =~ m/$zfmlextract/s ) { push @{ $stuff->{$1} }, { $2 => $3 }; } }} # end

Chug through

it

Test::ZFML

Page 178: Testing Code and Assuring Quality

sub _get_includes { my( $name, $file, $fromfile ) = @_; my $filepath = "$webroot/$file";

if( $filepath =~ /\$VERSION/ ){ $filepath =~ s/\$VERSION/$version/; }

if( $filepath =~ /\$LOCAL/ ){ my $path = $fromfile; $path =~ s/^.+?\/(.+)\/[a-z\.]+$/$version\/$1/; $filepath =~ s/\$LOCAL/$path/; }

my $tb = Test::ZFML->builder(); $tb->ok( -e $filepath, "Inlude/Template file ($filepath) Exists" );

...

Test::ZFML

Page 179: Testing Code and Assuring Quality

sub _get_includes { my( $name, $file, $fromfile ) = @_; my $filepath = "$webroot/$file";

if( $filepath =~ /\$VERSION/ ){ $filepath =~ s/\$VERSION/$version/; }

if( $filepath =~ /\$LOCAL/ ){ my $path = $fromfile; $path =~ s/^.+?\/(.+)\/[a-z\.]+$/$version\/$1/; $filepath =~ s/\$LOCAL/$path/; }

my $tb = Test::ZFML->builder(); $tb->ok( -e $filepath, "Inlude/Template file ($filepath) Exists" );

...

Process included

files

Test::ZFML

Page 180: Testing Code and Assuring Quality

...

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

Page 181: Testing Code and Assuring Quality

...

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 );}

Evaluate, evaluate, evaluate

Test::ZFML

Page 182: Testing Code and Assuring Quality

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; for my $hunk ( keys %{$stuff} ) { for my $evals ( @{ $stuff->{$hunk} } ) { for my $var ( keys %{$evals} ) { _evaluate_code( $p, $hunk, $var, $evals->{$var}, $file, $desc ); } } }

# loads everything into memory for testing require $_ for @cov_files; ## no critic}

Test::ZFML

Page 183: Testing Code and Assuring Quality

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; for my $hunk ( keys %{$stuff} ) { for my $evals ( @{ $stuff->{$hunk} } ) { for my $var ( keys %{$evals} ) { _evaluate_code( $p, $hunk, $var, $evals->{$var}, $file, $desc ); } } }

# loads everything into memory for testing require $_ for @cov_files; ## no critic}

Really evaluate

it

Test::ZFML

Page 184: Testing Code and Assuring Quality

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;

# Writing the contents out to a file so I can run # the tests with Devel::Cover turned on. open my $cov, '>', ".$filename"; print {$cov} $hunk; close $cov; push @cov_files, ".$filename"; eval "require '.$filename';"; ## no critic $tb->ok( ! $@, "$desc chunk ( $filename ) $@" ); eval "testzfml::$subname( \$p );"; die "eval failed - $@" if $@;}

Test::ZFML

Page 185: Testing Code and Assuring Quality

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;

# Writing the contents out to a file so I can run # the tests with Devel::Cover turned on. open my $cov, '>', ".$filename"; print {$cov} $hunk; close $cov; push @cov_files, ".$filename"; eval "require '.$filename';"; ## no critic $tb->ok( ! $@, "$desc chunk ( $filename ) $@" ); eval "testzfml::$subname( \$p );"; die "eval failed - $@" if $@;}

Write out files for Code Coverage

Test::ZFML

Page 186: Testing Code and Assuring Quality

sub _wrap_hunk { my( $hunk, $subname ) = @_;

# HEREDOCs inside eval aren't recognizable as HEREDOCs. # This re-quotes HEREDOCs as q()/qq() strings. if( $hunk =~ m/<</s ) {

# replace all intended quoting chars with an HTML entity $hunk =~ s/\|/&#124;/gs; $hunk =~ s/=\s* # start of an assignment << # involving a heredoc ('|") # using a quoting delimiter

(?{ $1 eq q(") ? 'qq' : 'q' }) # which we'll remember in $^R

([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

Page 187: Testing Code and Assuring Quality

sub _wrap_hunk { my( $hunk, $subname ) = @_;

# HEREDOCs inside eval aren't recognizable as HEREDOCs. # This re-quotes HEREDOCs as q()/qq() strings. if( $hunk =~ m/<</s ) {

# replace all intended quoting chars with an HTML entity $hunk =~ s/\|/&#124;/gs; $hunk =~ s/=\s* # start of an assignment << # involving a heredoc ('|") # using a quoting delimiter

(?{ $1 eq q(") ? 'qq' : 'q' }) # which we'll remember in $^R

([A-Z]+) # next the heredoc token \1; # close quoting delimiter (.*?)\n # the heredoc \2 # closing heredoc token /= $^R|$3|;/gsx; # replace with quoting }

...

Wrap Heredocs

Test::ZFML

Page 188: Testing Code and Assuring Quality

...

my $chunk;

# wrap the hunk with its own package, strictures and # warnings enabled, a sigwarn handler that causes eval # errors ($@) to throw a test ok() error, and callable via a # subroutine call. $chunk = <<"EOC";package testzfml;use strict;use warnings;use ZFML;BEGIN { \$SIG{'__WARN__'} = sub { die \$_[0] } } ## no criticsub $subname {$hunk}1;EOC

return $chunk;}

Test::ZFML

Page 189: Testing Code and Assuring Quality

...

my $chunk;

# wrap the hunk with its own package, strictures and # warnings enabled, a sigwarn handler that causes eval # errors ($@) to throw a test ok() error, and callable via a # subroutine call. $chunk = <<"EOC";package testzfml;use strict;use warnings;use ZFML;BEGIN { \$SIG{'__WARN__'} = sub { die \$_[0] } } ## no criticsub $subname {$hunk}1;EOC

return $chunk;}

Wrap it in it's own

namespace

Test::ZFML

Page 190: Testing Code and Assuring Quality

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

Page 191: Testing Code and Assuring Quality

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

Page 192: Testing Code and Assuring Quality

sub _critique_code { my( $eval_init, $name, $hunk, $desc ) = @_; my $subname = "$eval_init$name"; my $problems = 0;

$hunk = _wrap_hunk( $hunk, $subname ); my $tb = Test::ZFML->builder;

for my $violation ( critique( { -severity => 1, -verbose => 1 }, \$hunk ) ){ $tb->ok( ! $violation, "Critique problem: $violation" ); $problems++; } $tb->ok( ! $problems, "$desc chunk ( $subname )" ); return;}

Test::ZFML

Page 193: Testing Code and Assuring Quality

sub _critique_code { my( $eval_init, $name, $hunk, $desc ) = @_; my $subname = "$eval_init$name"; my $problems = 0;

$hunk = _wrap_hunk( $hunk, $subname ); my $tb = Test::ZFML->builder;

for my $violation ( critique( { -severity => 1, -verbose => 1 }, \$hunk ) ){ $tb->ok( ! $violation, "Critique problem: $violation" ); $problems++; } $tb->ok( ! $problems, "$desc chunk ( $subname )" ); return;}

Report violations

Test::ZFML

Page 194: Testing Code and Assuring Quality

sub replace_ok { my( $file, $p, $desc ) = @_; croak 'wrong file' if $file ne $file_to_test; my $tb = Test::ZFML->builder;

for my $var (@vars) { my $varname = $var; $varname =~ s/^\$//; my $pname = $p->var->{$varname}; $tb->ok( $p->var->{$varname}, "$varname found in $file" ); $file_contents =~ s/\Q$var\E/$pname/g; } my %input = %{ $p->input }; $file_contents =~ s/\$(input\{)'?([A-Za-z_]+)'?\}/\$$1$2}/g; eval "\$file_contents = qq|$file_contents|;";}

Test::ZFML

Page 195: Testing Code and Assuring Quality

sub replace_ok { my( $file, $p, $desc ) = @_; croak 'wrong file' if $file ne $file_to_test; my $tb = Test::ZFML->builder;

for my $var (@vars) { my $varname = $var; $varname =~ s/^\$//; my $pname = $p->var->{$varname}; $tb->ok( $p->var->{$varname}, "$varname found in $file" ); $file_contents =~ s/\Q$var\E/$pname/g; } my %input = %{ $p->input }; $file_contents =~ s/\$(input\{)'?([A-Za-z_]+)'?\}/\$$1$2}/g; eval "\$file_contents = qq|$file_contents|;";}

Replace special

variables

Test::ZFML

Page 196: Testing Code and Assuring Quality

Test::ZFML

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 );}

Page 197: Testing Code and Assuring Quality

Test::ZFML

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 its'

contents

Page 198: Testing Code and Assuring Quality

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

Page 199: Testing Code and Assuring Quality

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

Page 200: Testing Code and Assuring Quality

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

Page 201: Testing Code and Assuring Quality

sub html_ok { my( $file, $desc ) = @_; croak 'wrong file' if $file ne $file_to_test; Test::HTML::Lint::html_ok( $file_contents, $desc ); return;}

Check the HTML

Test::ZFML

Page 202: Testing Code and Assuring Quality
Page 203: Testing Code and Assuring Quality

Putting it all together

• Test::More

• prove

• Perl::Critic

• Devel::Cover

• Makefile

• Test::ZFML

Page 204: Testing Code and Assuring Quality

The Test File

Screencast demonstration

removedfor PDF

Page 205: Testing Code and Assuring Quality

The MakefileKent-Cowgills-Computer(~/acmecorp)$ cat Makefile OPENCMD = openBROWSER = /Applications/Safari.appclean: cover -deletetest: 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 reportreport: $(OPENCMD) $(BROWSER) cover_db/coverage.html

Page 206: Testing Code and Assuring Quality

Running Tests

Kent-Cowgills-Computer(~/acmecorp)$ make testprove t/t/artdesign-zfml....ok t/index-zfml........ok t/testlogin-zfml....ok All tests successful.Files=3, Tests=59, 2 wallclock secs ( 1.24 cusr + 0.20 csys = 1.44 CPU)

Page 207: Testing Code and Assuring Quality

Test Failure

Kent-Cowgills-Computer(~/acmecorp)$ make testprove t/t/artdesign-zfml....ok 1/29 # Failed test 'HTML passes Lint test'# in /Users/kentcowgill/acmecorp/lib/Test/ZFML.pm at line 136.

Page 208: Testing Code and Assuring Quality

Test Code CoverageKent-Cowgills-Computer(~/acmecorp)$ make covermake cleancover -deleteDeleting database /Users/kentcowgill/acmecorp/cover_dbPERL5OPT=-MDevel::Cover=-ignore,prove,+ignore,lib.*\.pm,+ignore,zfml\.t make testprove t/t/artdesign-zfml....ok t/index-zfml........ok t/testlogin-zfml....ok All tests successful.Files=3, Tests=59, 22 wallclock secs (18.46 cusr + 0.48 csys = 18.94 CPU)coverReading database from /Users/kentcowgill/acmecorp/cover_dbDevel::Cover: merging data for .artdesign.zfml.EVALCOPYRIGHT_YEAR into .index.zfml.EVALCOPYRIGHT_YEAR

Page 209: Testing Code and Assuring Quality

Test Code Coverage

---------------------------- ------ ------ ------ ------ ------ ------ ------File stmt bran cond sub pod time total---------------------------- ------ ------ ------ ------ ------ ------ ------.artdesign.zfml.INITSETUP 100.0 n/a n/a 100.0 n/a 12.9 100.0.index.zfml.EVALCOPYRIGHT_YEAR 92.3 n/a n/a 100.0 n/a 63.9 94.4.index.zfml.INITSETUP 100.0 n/a n/a 100.0 n/a 11.5 100.0.testlogin.zfml.INITFORM 100.0 75.0 n/a 100.0 n/a 11.8 96.0Total 98.3 75.0 n/a 100.0 n/a 100.0 97.6---------------------------- ------ ------ ------ ------ ------ ------ ------

Writing HTML output to /Users/kentcowgill/acmecorp/cover_db/coverage.html ... done.make reportopen /Applications/Safari.app cover_db/coverage.html

Continued...

Page 210: Testing Code and Assuring Quality

Test Code Coverage

Page 211: Testing Code and Assuring Quality

Sweet!=D

Page 212: Testing Code and Assuring Quality

Functional Testing

Page 213: Testing Code and Assuring Quality

Introducing Test::WWW::MechanizeTest::WWW::Mechanize(3) Test::WWW::Mechanize(3)

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" );

Page 214: Testing Code and Assuring Quality

Planetary::DblClick_tag.pm#!/usr/bin/perl

use strict;use warnings;

#use Test::More tests => 40;use Test::More 'no_plan';use Test::WWW::Mechanize;

...

Page 215: Testing Code and Assuring Quality

Planetary::DblClick_tag.pm#!/usr/bin/perl

use strict;use warnings;

#use Test::More tests => 40;use Test::More 'no_plan';use Test::WWW::Mechanize;

# .fwpwd contains my AcmeCorp user id and passwordour( $AcmeCorp_username, $AcmeCorp_password );require '/Users/kentcowgill/acmecorp/.fwpwd';

# create a new Test::WWW:Mechanize object.my $ua = Test::WWW::Mechanize->new;

...

Page 216: Testing Code and Assuring Quality

Planetary::DblClick_tag.pm...

# first, get the home page$ua->get_ok( "http://test.AcmeCorp.com", "Check base URL" );

# log in (using kcowgill credentials)$ua->form_number( 1 );$ua->field( 'EMAIL_ADDRESS', $AcmeCorp_username );$ua->field( 'PASSWORD', $AcmeCorp_password );$ua->click( 'returnLogin' );

# basic sanity check that we're on the right page (/AcmeCorp/my/index)$ua->content_contains( "Hi, Kent!", "received greeting message" );

...

Page 217: Testing Code and Assuring Quality

Planetary::DblClick_tag.pm...

# grab the iframe src tagmy( $iframe ) = $ua->content =~ m/iframe .*src="([^"]+)"/;

# make sure it's got the right stuff in it.like( $iframe, qr/site=fw/, 'got site=fw in iframe src tag' );like( $iframe, qr/affiliate=fw/, 'got affiliate=fw in iframe src tag' );like( $iframe, qr/app=(?:my|other)/, 'got app=my in iframe src tag' );

...

Page 218: Testing Code and Assuring Quality

Planetary::DblClick_tag.pm$ prove -v dblclick.t dblclick....ok 1 - Check base URLok 2 - received greeting messageok 3 - got site=ac in iframe src tagok 4 - got affiliate=ac in iframe src tagok 5 - got app=tango in iframe src tagok 6 - got size=160x600 in iframe src tagok 7 - got pp=1 in iframe src tagok 8 - got path=$ID in iframe src tagok 9 - got dcpc=606 in iframe src tagok 10 - got ge=2 in iframe src tagok 11 - got age=19 in iframe src tagok 12 - got widget=14 in iframe src tagok 13 - got wango=5 in iframe src tagok 14 - got state=30 in iframe src tagok 15 - got tango=3.8 in iframe src tagok 16 - got doodad=0 in iframe src tagok 17 - got gadget=0075 in iframe src tagok 18 - got mtfnpy=4 in iframe src tag...okAll tests successful.Files=1, Tests=38, 8 wallclock secs ( 0.27 cusr + 0.08 csys = 0.35 CPU)

Page 219: Testing Code and Assuring Quality

How To Get

Started

Page 220: Testing Code and Assuring Quality

The VIM Plugin

Page 221: Testing Code and Assuring Quality

The VIM Plugin

Screencast demonstration

removedfor PDF

Page 222: Testing Code and Assuring Quality

The VIM Plugin

Page 223: Testing Code and Assuring Quality

The Perl Module

Page 224: Testing Code and Assuring Quality

The Perl Module

Screencast demonstration

removedfor PDF

Page 225: Testing Code and Assuring Quality

The Perl Module

Page 226: Testing Code and Assuring Quality

~fin~

Page 227: Testing Code and Assuring Quality
Page 228: Testing Code and Assuring Quality

References•Wikipedia:http://en.wikipedia.org/wiki/Software_testinghttp://en.wikipedia.org/wiki/Test_Case

•Testing Reference card:http://pub.langworth.com/perl_test_refcard.pdf

•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/

•Screencast software (Mac only):http://www.ambrosiasw.com/utilities/snapzprox/

•Cats:http://icanhascheezburger.com/