NAME
App::Test::Generator::PodExampleExtractor - Extract runnable code examples from a Perl module's POD
SYNOPSIS
use App::Test::Generator::PodExampleExtractor;
my $ex = App::Test::Generator::PodExampleExtractor->new(
file => 'lib/My/Module.pm',
);
my $examples = $ex->extract();
for my $e (@$examples) {
printf "%-30s %s\n", $e->{label}, $e->{code};
}
DESCRIPTION
Parses the POD of a Perl module and returns a structured list of runnable code examples. Three sources are collected:
Verbatim blocks inside
=head1 SYNOPSISand=head2 SYNOPSIS=for example begin...=for example endblocksAnnotated single-line call examples inside per-method docstrings (lines matching
$obj->method(...) # returns valueormethod(...) # =value>)
Return-value annotations of the form # returns value or # => value are parsed and exposed as expected in the result hashref, enabling downstream test generators to emit is() assertions.
new
Construct a new extractor for the given source file.
my $ex = App::Test::Generator::PodExampleExtractor->new(
file => 'lib/My/Module.pm',
);
Arguments
filePath to the Perl module to extract examples from. Required. The file must exist on disk.
Returns
A blessed App::Test::Generator::PodExampleExtractor object. Croaks if file is missing or does not exist.
EXAMPLE
use App::Test::Generator::PodExampleExtractor;
my $ex = App::Test::Generator::PodExampleExtractor->new(
file => 'lib/Acme/Widget.pm',
);
printf "Extracting examples from %s\n", 'lib/Acme/Widget.pm';
MESSAGES
file is required-
filewas not supplied. File not found: $path-
filewas supplied but the path does not exist on disk.
API specification
input
{ file => { type => SCALAR } }
output
{ type => OBJECT, isa => 'App::Test::Generator::PodExampleExtractor' }
FORMAL SPECIFICATION
Pre: defined file ∧ -f file
Post: ref(result) eq 'App::Test::Generator::PodExampleExtractor' ∧ result->{file} eq file
extract
Extract all runnable examples from the module's POD.
my $examples = $ex->extract();
Arguments
None beyond $self.
Returns
An arrayref of example hashrefs, deduplicated by code text. Each hashref has the following keys:
label— human-readable name for use as a test label (e.g."SYNOPSIS example 1")section— the POD section heading (=head1/=head2text) where the example was foundcode— the raw code text, dedented. May be multi-line for verbatim blocks.expected— the expected return value string from a# returns valueor# => valueannotation;undefif not annotated.annotated_line— for annotated single-line examples, the bare expression with the annotation stripped;undeffor verbatim blocks.
EXAMPLE
my $examples = $ex->extract();
for my $e (@{$examples}) {
printf "[%s] %s\n", $e->{label}, $e->{code};
if(defined $e->{expected}) {
printf " expects: %s\n", $e->{expected};
}
}
# Count examples without a declared return value
my @unannotated = grep { !defined $_->{expected} } @{$examples};
printf "%d unannotated examples\n", scalar @unannotated;
MESSAGES
File not found: $path(raised byFile::Slurp::read_file)-
The source file disappeared between construction and the
extractcall.
API specification
input
{ self => { type => OBJECT, isa => 'App::Test::Generator::PodExampleExtractor' } }
output
{ type => ARRAYREF }
FORMAL SPECIFICATION
Post: result is an arrayref where: ∀ e ∈ result: defined e->{label} ∧ defined e->{section} ∧ defined e->{code} ∧ no two entries share the same code value (deduplication)
COMMON PITFALLS
- Shell-command blocks are silently dropped
-
Verbatim paragraphs that contain only shell commands (e.g.
prove -l t/orextract-schemas lib/My/Module.pm) are filtered out by_looks_like_perl. They would cause compile errors underuse strictin a generated test file. If you want a shell command to appear as an example, put it in a POD comment block, not a verbatim paragraph. - Annotations must be on the same line as the call expression
-
The
# returns/# =>annotation is parsed as a line-level suffix. Multi-line calls spread across multiple lines will not pick up an annotation on the last line — only the last line is examined, and the call expression on earlier lines is lost. expectedis always a raw string-
The
expectedfield is the literal text captured after# returnsor# =>. It is not evaluated or type-converted. Downstream code that emitsis($result, $expected)must handle quoting appropriately. - SYNOPSIS blocks from
=head3and deeper are not collected -
Only
=head1 SYNOPSISand=head2 SYNOPSISsections are scanned for verbatim blocks. Deeper heading levels are ignored.
LIMITATIONS
- No evaluation of verbatim blocks
-
Verbatim blocks are returned as raw code strings. The module does not evaluate them or check that they are syntactically valid Perl. Blocks that are syntactically broken will cause the downstream test file to fail to compile.
- Deduplication is by exact
codetext -
Two examples with identical
codestrings are deduplicated even if they come from different sections. If the same one-liner appears in both SYNOPSIS and a method docstring, only the first occurrence is kept.
SEE ALSO
bin/pod-example-tester- App::Test::Generator
- Pod::Simple
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
LICENCE AND COPYRIGHT
Copyright 2026 Nigel Horne.
Usage is subject to the terms of GPL2. If you use it, please let me know.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 110:
Non-ASCII character seen before =encoding in '∧'. Assuming UTF-8