Testing with Test::More

Perl Intermediate/Advanced — Testing with Test::More
Perl Intermediate/Advanced
Course 2 · Chapter 8 · Testing with Test::More

🧪 Testing with Test::More

Perl has one of the oldest, most established testing cultures in programming — TAP (Test Anything Protocol), the output format Test::More produces, originated in Perl and was later adopted as a language-agnostic standard used by testing tools well beyond Perl itself. This chapter covers writing and running real tests.

✅ The Basic Assertions

use Test::More; ok(1 + 1 == 2, 'basic math works'); # true/false — the most basic assertion is(2 + 2, 4, 'addition is correct'); # equality — reports BOTH values on failure isnt(2 + 2, 5, 'addition is not wrong'); # inequality like("Hello World", qr/World/, 'contains World'); # regex match — reuses Chapter 1's m// and qr//

is() is preferred over ok($got == $expected, ...) for equality checks specifically because a failure reports both the actual and expected values automatically — genuinely more useful for diagnosing what went wrong than a bare pass/fail from ok alone.

📝 A First Test File

# t/basic.t use strict; use warnings; use Test::More; is(1 + 1, 2, 'one plus one is two'); ok("perl" eq "perl", 'strings match'); done_testing(); # modern style — no need to declare a plan count up front

Test files conventionally live in a t/ directory and end in .t. done_testing(); at the end is the modern convention, replacing the older style of declaring use Test::More tests => 5; and having to keep that count manually in sync with the actual number of assertions.

▶️ Running Tests with prove

prove t/basic.t # run one test file prove -r t/ # recursively run every .t file in the t/ directory
t/basic.t .. ok All tests successful. Files=1, Tests=2, 0 wallclock secs

prove (installed alongside Perl itself) is the standard test runner — it executes every matched .t file and reports a clean pass/fail summary.

🏗️ Testing Data Structures

my $got = { name => "Ada", age => 28 }; my $expected = { name => "Ada", age => 28 }; is_deeply($got, $expected, 'hashref contents match'); # PASSES — compares contents, not identity
⚠ is() on References Checks Identity, Not Contents

is($got, $expected, ...) on two references — even two hashrefs holding identical data — compares whether they're the exact same reference in memory (Course 1, Chapter 9's reference-vs-value distinction), not whether their contents match. Two separately-built hashrefs with identical keys and values will fail a plain is() check. is_deeply() is the correct tool for comparing the actual contents of nested arrays/hashes, recursively.

🔧 A Worked Example — Testing a Real Subroutine

# t/multiplier.t use strict; use warnings; use Test::More; sub make_multiplier { # reused from Chapter 7 my ($factor) = @_; return sub { my ($n) = @_; return $n * $factor; }; } my $double = make_multiplier(2); is($double->(5), 10, 'double(5) is 10'); is($double->(0), 0, 'double(0) is 0'); like("Result: " . $double->(3), qr/\d+/, 'result contains a number'); done_testing();

This exercises a closure (Chapter 7) directly through Test::More's assertions — is() for the expected numeric results, like() confirming the output's shape via regex rather than an exact string match.

TAP: Perl's Testing Format, Adopted Widely

TAP (Test Anything Protocol) — the simple, line-based pass/fail output Test::More produces — originated in Perl's testing culture and was later formalized as a language-agnostic standard, with TAP producers and consumers built for many other languages beyond Perl. This is a genuine parallel to CPAN's own historical head start (Chapter 5): Perl's testing tooling matured early enough to influence testing practices well outside the language itself, much like PCRE's regex engine did.

ok / is / isnt / like

The core assertions — is() for equality reports both values on failure.

done_testing()

Modern style — no upfront test-count plan to keep in sync.

prove

The standard test runner — prove -r t/ runs every test file.

is_deeply

Compares reference contents recursively — is() alone only checks identity.

💻 Coding Challenges

Challenge 1: A Basic Test File

Write a test file t/strings.t with at least 3 assertions covering string concatenation, a regex match with like, and one intentionally-failing assertion (to see what a failure looks like) — then remove the failing one and confirm all tests pass with prove.

→ Solution

Challenge 2: Test a Subroutine From Course 1

Write a test file testing Course 1, Chapter 7's power subroutine (base and optional exponent, defaulting to 2) with at least 3 is() assertions covering both the default and an explicit exponent.

→ Solution

Challenge 3: is vs is_deeply

Given two separately-built arrayrefs both containing (1, 2, 3), write one assertion using is() (predict and note that it fails) and one using is_deeply() (predict and note that it passes), and explain in one sentence why they disagree.

→ Solution

🎯 What's Next

Final chapter: Perl One-Liners & Practical Text Processingperl -e/-pe/-ne, closing the loop back to the Bash/sed/awk cluster.