Test::Effects(3pm)
okay bestie, sit DOWN, because we're about to read a manpage together and honestly? it's a good one. This is Test::Effects β the Test::Effects(3pm) page, from the User Contributed Perl Documentation corner of the universe. π
π NAME π
Test::Effects β test all the effects at once, bestie. Return value, I/O, warnings, exceptions, literally all of it. β¨
π VERSION π
This document describes Test::Effects version 0.002000. no cap, that's the number right there. π
πΈ SYNOPSIS πΈ
okay so here's the vibe, three flavours of the same slay:
use Test::Effects;
# Test all possible detectable side-effects of some code...
effects_ok { your_code_here() }
{
return => $expected_scalar_context_return_value,
warn => qr/match expected warning text/,
stdout => '', # i.e. Doesn't print anything.
}
=> 'Description of test';
# Test only specifically requested side-effects of some code...
effects_ok { your_code_here() }
ONLY {
return => \@expected_list_context_return_values,
stderr => 'Expected output to STDERR',
die => undef, # i.e. Doesn't die.
exit => undef, # i.e. Doesn't exit either.
}
=> 'Description of test';
# Test that some code has no detectable side-effects...
effects_ok { your_code_here() };
three little baddies, three different jobs. the first one checks everything it can detect, the second one is like "actually only check these specific things" (hi ONLY), and the third is just "this code should do absolutely nothing extra, prove it." π¦
π¦ DESCRIPTION π¦
Test::Effects gives you exactly one exported subroutine, and its name is effects_ok. one icon. one job. periodt. π
This sub expects a block of code (or a sub ref) as its first argument, then an optional hash ref as its second, and an optional string as its third. three slots, in that order, don't mix 'em up. π
π arg one: the code π
The first argument is the code you wanna test. by default it runs in void context, but lowkey it can also get called in list context or scalar context instead β that depends entirely on the test specification you hand over in the second argument. you're the one driving. πβ¨
The block gets run inside a call to Test::Trap::trap(), which is the whole reason this works: every warning, every exception, all the output, and any exit attempt gets trapped. nothing escapes. π
π and yes β the block is allowed to contain calls to other Test::Builder-based testing modules, and those get handled correctly inside the overall test. no chaos, no double-counting. iconic behaviour, honestly.
π arg two: the expectations π
The second argument is a hash reference, and its entries say what side-effects you expect from running that block. the setup is: the key is the name of the side-effect you care about, and the value is the "effect" you expected. matchy matchy. π
Here's the part that's actually so smart: any side-effect you don't explicitly specify gets automatically tested for default behaviour β meaning no warnings, no exceptions, no output, no call to exit(), all of that good stuff. it just assumes the boring answer unless you tell it otherwise. π
And if you omit the entire hash? then all possible side-effects get tested for default behaviour. in other words, the question it's asking is: did this block of code have no side-effects whatsoever? π
π§ arg three: the description π§
The third argument is the overall description of the test β y'know, the usual final argument you'd pass to a Perl test. π
If you skip it, effects_ok() will generate a description for you based on the line number at which it was called. so it always has something to call itself, no stress. π
use Test::Effects;
Loads the module up, and exports the effects_ok(), VERBOSE(), ONLY(), and TIME() subroutines (they're all explained further down, bestie π). It also exports the whole standard interface from the Test::More module. Two-for-one, we love to see it.
use Test::Effects tests => $N;
Literally the exact same thing as:
use Test::More tests => $N;
No cap β use Test::Effects can take all the same arguments that use Test::More takes. It's the same energy, just with extra sprinkles. β¨
use Test::Effects import => [':minimal'];
Only exports the effects_ok() subroutine.
Do not export VERBOSE(), ONLY(), TIME(), or any of the Test::More interface. Just the one thing. Minimalist queen. π
use Test::Effects import => [':more'];
Only exports the effects_ok() subroutine plus the Test::More interface.
Do not export VERBOSE(), ONLY(), or TIME().
effects_ok {BLOCK} {EFFECTS_HASH} => 'TEST_DESCRIPTION';
The main character of this whole operation, fr fr. π
It tests the code inside the block, making sure that the side-effects named by the keys of the hash actually match up with the corresponding hash values. Both the hash argument and the description argument are optional β you can skip them, no judgement. π«Ά
πΈ the effects you can specify as key/value pairs
void_return => undefβ calls the block in void context. It runs, it does its thing, nobody's collecting a return value.return => $ARRAY_REFERENCElist_return => $ANY_SCALAR_VALUEβ calls the block in list context. The final value evaluated in the block should (deeply) match the array ref or the scalar value you gave to the option.return => $NON_ARRAYREFscalar_return => $ANY_SCALAR_VALUEβ calls the block in scalar context. The final value evaluated in the block should match the scalar value of the option.stdout => $STRINGβ whatever the block wrote toSTDOUTshould beeqto $STRING. Exact match, bestie.stdout => $REGEXβ whatever the block wrote toSTDOUTshould match $REGEX.stdout => $CODE_REFβ the subroutine that code ref points to should return true when it gets passed whatever the block wrote toSTDOUT.
That subroutine is allowed to call nested tests (like Test::More's is or Test::Tolerant's is_tol) and those get handled correctly. Nested tests inside nested tests, it's giving inception. π
stderr => $STRINGstderr => $REGEXstderr => $CODE_REFβ whatever the block writes toSTDERRshouldmatchthe value you specified (eithereq, or=~, or returning true when passed as an argument).
Heads up: if you don't specify this option, but you do specify thewarnoption (scroll down a lil β¬οΈ), then this option just defaults to whatever thewarnoption's value was. Convenient? Yes. Surprising? Also yes. π
warn => $STRINGwarn => $REGEXwarn => $CODE_REFwarn => [ $STRING1, $REGEX2, $CODE_REF3, $ETC ]β the block should throw the exact number of warnings you specified, and each one of those warnings should match the corresponding value you gave, in the order you gave them. Order matters, periodt. β¨die => $STRINGdie => $REGEXdie => $CODE_REFβ the block should raise an exception, and that exception should match the value you specified.
Note: when you're dealing with OO exceptions, use a code ref to test them:
die => sub { shift->isa('X::BadData') }
You can also use Test::More-ish tests if that's more your vibe:
die => sub { isa_ok(shift, 'X::BadData') }
exit => $NUMBERexit => $REGEXexit => $CODE_REFβ the block should callexit()and the exit code should match the value you specified.timing => { min => $MIN_SEC, max => $MAX_SEC }timing => [$MIN_SEC, $MAX_SEC]timing => $MAX_SECβ this option does a separate timing measurement for the block, by running it over and over across at least 1 cpu-second and averaging out the times each run took (yep, exactly like the Benchmark module does it). β¨
When you hand it a hash reference, the 'min' and 'max' entries set the range of allowable timings (in seconds) for the block. Like so:
# Test our new snooze() function...
effects_ok { snooze(1.5, plus_or_minus=>0.2); }
{
timing => { min => 1.3, max => 1.7 },
}
=> 'snooze() slept about the right amount of time';
The default for 'min' is zero seconds; the default for 'max' is eternity. Eternity!! Iconic default tbh. π
If you use an array reference instead of a hash reference, the first value in the array counts as the minimum time, and the final value counts as the maximum allowed time. So the time spec above could also be written:
timing => [ 1.3, 1.7 ],
But do not write:
timing => [ 1.3 .. 1.7 ],
(unless your limits are integers, bestie) because Perl truncates the bounds of a range, so it treats [1.3 .. 1.7] as [1 .. 1]. Sneaky little bug, we hate to see it. π
If you use a number instead of a reference, then that number counts as the maximum time allowed:
timing => 3.2, # Same as: timing => { min => 0, max => 3.2 }
And if you don't specify either time limit:
timing => {},
or:
timing => [],
then the "zero-to-eternity" defaults kick in and effects_ok() just times the block and reports the timing (as a success). Free info, no test. π
Note that the timings you get from this option are considerably more accurate than the ones the TIME => 1 option produces (see below), but they also take significantly longer to measure. Accuracy costs time, always. π
πΈ other configuration options (also key/value pairs in the hash)
VERBOSE => $BOOLEANβ if the value is true, all the side-effect tests get reported individually (by running them in a subtest).
When this option is false (or you just... don't include it), only the overall result gets reported, plus any individual failures. Quiet mode. π€«
ONLY => $BOOLEANβ if the value is true, only the effects you explicitly requested with the other keys of this hash get tested for. In other words, this option makeseffects_ok()skip the "default" tests for the unnamed side-effects.
When this option is false (or omitted) unspecified options get tested for their expected default behaviour.
TIME => $BOOLEANβ if the value is true, the block gets timed as it's executed. The timing shows up in the final TAP line. β±οΈ
Note that this option is entirely independent of the 'timing' option (the one that times the block repeatedly and then tests it against the limits you set).
In contrast, the 'TIME' option just times the block once, while it's being evaluated for all the other tests. That's way less accurate, but also way faster and way less intrusive β perfect when you only want a rough indication of performance. Low effort, still useful. π
WITHOUT => 'Module::Name'WITHOUT => qr/Module::.*/β executes the block as if the specified module (or every module matching the specified pattern) weren't installed at all. Bye bestie, you don't exist here. π¦WITHOUT => 'lib/path/'WITHOUT => qr{lib/*}β executes the block as if the specified library directory (or every directory matching the specified pattern) weren't accessible.
The path you specify has to include at least one slash ("/"), otherwise it gets interpreted as a module name (see above). You can always tack a redundant slash onto the end of the path if you need to:
WITHOUT => 'lib', # Test without the C<lib.pm> module
WITHOUT => 'lib/', # Test without the C<lib> directory
VERBOSE $HASH_REF
A call to:
effects_ok { BLOCK }
VERBOSE { return => 0, stdout => 'ok' }
is just a shorthand for:
effects_ok { BLOCK }
{ return => 0, stdout => 'ok', VERBOSE => 1 }
Same thing, fewer characters. Efficiency queen. β¨
ONLY $HASH_REF
A call like:
effects_ok { BLOCK }
ONLY { return => 0, stdout => 'ok' }
is just a shorthand for:
effects_ok { BLOCK }
{ return => 0, stdout => 'ok', ONLY => 1 }
TIME $HASH_REF
A call like:
effects_ok { BLOCK }
TIME { return => 0, stdout => 'ok' }
is just a shorthand for:
effects_ok { BLOCK }
{ return => 0, stdout => 'ok', TIME => 1 }
Note that theVERBOSE,ONLY, andTIMEsubs can be "stacked" β in any combination and any order you want:
effects_ok { BLOCK }
TIME ONLY VERBOSE { return => 0, stdout => 'ok' }
effects_ok { BLOCK }
VERBOSE ONLY { return => 0, stdout => 'ok' }
Stack them like bracelets, bestie. π
use Test::Effects::VERBOSE;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had a VERBOSE => 1 option set. Set it once, done. π«Ά
Note, however, that an explicit VERBOSE => 0 in any call in the scope overrides this default. Specific beats general, always.
no Test::Effects::VERBOSE;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had a VERBOSE => 0 option set. And again, an explicit VERBOSE => 1 in any call in the scope overrides this default. π
use Test::Effects::ONLY;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had a ONLY => 1 option set.
Note, however, that an explicit ONLY => 0 in any call in the scope overrides this default.
no Test::Effects::ONLY;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had a ONLY => 0 option set. Again, though β an explicit ONLY => 1 in any call in the scope overrides this default.
use Test::Effects::TIME;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had a TIME => 1 option set.
Note, however, that an explicit TIME => 0 in any call in the scope overrides this default.
no Test::Effects::TIME;
This makes every subsequent call to effects_ok() in the current lexical scope act as if it had no TIME => 0 option set. Again, though, an explicit TIME => 1 in any call in the scope overrides this default.
π DIAGNOSTICS π
"Second argument to effects_ok() must be hash or hash reference, not %s"βeffects_ok()expects a hash as its second argument, but you handed it something else entirely. π
This can happen if you forget to put braces around a single option, like this:
effects_ok { test_code() }
warn => qr/Missing arg/;
That needs to be:
effects_ok { test_code() }
{ warn => qr/Missing arg };
Or maybe you accidentally used an array instead of a hash:
effects_ok { test_code() }
[ warn => qr/Missing arg ];
That is not supported, because it's being reserved for a future feature. Don't do it. Future-you will thank present-you. π
"Invalid timing specification: timing => %s"β the'timing'option expects a hash reference, an array reference, or a single number as its value. You specified something else, andeffects_ok()therefore had no idea what you were saying to it. π
π CONFIGURATION AND ENVIRONMENT π
Test::Effects requires no configuration files and no environment variables. Zero setup, zero fuss, just vibes. π§β¨
πΈ DEPENDENCIES πΈ
okay so before u even think about running this, here's the vibe check on what needs to already be in ur life π
- Perl 5.14 or better. like, 5.14 is the floor bestie, don't come at me with anything older π
- Test::Trap, version
v0.2.1or better. that's the whole backbone of the operation, no Test::Trap no effects testing, periodt β¨
π INCOMPATIBILITIES π
None reported. π
that's it. that's the section. clean sheet, no drama, we love a peaceful coexistence moment π«Ά
π BUGS AND LIMITATIONS π
okay so this is the iconic part. the irony is genuinely giving main character energy:
the test suite for this module is, as yet, unsatisfactory. π
and the official word from T.D.D. Barbie herself:
"Testing test modules is HARD!"
likeβ¦ yeah. she said it. no cap. testing a thing that tests things is a whole recursive nightmare and we're all just living in it π
No other bugs have been reported.
got a bug? a feature request? a little something u need to share with the group chat? report it to bug-test-effects@rt.cpan.org, or slide into the web interface at http://rt.cpan.org π
π AUTHOR π
Damian Conway β DCONWAY@CPAN.org β¨
π LICENCE AND COPYRIGHT π
Copyright (c) 2012, Damian Conway DCONWAY@CPAN.org. All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic π
π DISCLAIMER OF WARRANTY π
bestie. listen. this part is in ALL CAPS because that's legally how u have to yell it, and I'm not about to soften a single clause of it β so here it is, unhinged and unedited, exactly as the lawyers demand π
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
that's the whole page, bestie π¦ go forth and test ur effects, and remember: this lil doc came to u courtesy of perl v5.36.0, dated 2023-07-17, under the name Test::Effects(3pm) β mwah, stan, bye πβ¨π
π¦ where this page comes from
- debian trixie: libtest-effects-perl 0.002000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libtest-effects-perl 0.002000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libtest-effects-perl 0.002000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libtest-effects-perl 0.002000-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms