TEST2::MANUAL::TOOLING::NESTING(3pm)

πŸ¦‹ Test2::Manual::Tooling::Nesting(3pm) πŸ¦‹

okay bestie, this page is from the Perl Programmers Reference Guide, and it’s literally a tutorial for using other tools within your own. no cap. πŸ’…

✨ NAME ✨

  • Test2::Manual::Tooling::Nesting β€” Tutorial for using other tools within your own.

🌸 DESCRIPTION 🌸

okay so sometimes you’re out here writing the exact same test pattern over and over, and in those cases you might want to wrap the logic in a shiny new test function that calls a bunch of tools together. Sounds easy enough, right? It can still cause headaches if you don’t do it correctly, bestie. 😭

✨ NAIVE WAY ✨

Let’s say you find yourself writing the same test pattern over and over for multiple objects:

my $obj1 = $class1->new;
is($obj1->foo, 'foo', "got foo");
is($obj1->bar, 'bar', "got bar");

my $obj2 = $class1->new;
is($obj2->foo, 'foo', "got foo");
is($obj2->bar, 'bar', "got bar");

... 10x more times for classes 2-12

The naive way to do this is to write a check_class() function like this:

sub check_class {
    my $class = shift;
    my $obj = $class->new;
    is($obj->foo, 'foo', "got foo");
    is($obj->bar, 'bar', "got bar");
}

check_class($class1);
check_class($class2);
check_class($class3);
...

This will appear to work fine, and you might not notice any problems, so long as the tests are passing. πŸ™ˆ

😭 WHATS WRONG WITH IT? 😭

The problems with the naive approach become obvious the moment things start to fail. The diagnostics that tell you what file and line the failure occurred on will be wrong β€” the failure gets reported to the line inside check_class, not to the line where check_class() was called. That’s a problem because it leaves you with no idea which class is failing. πŸ’€

🫢 HOW TO FIX IT 🫢

Luckily this is extremely easy to fix. You need to acquire a context object at the start of your function, and release it at the end… yes it is that simple. ✨

use Test2::API qw/context/;

sub check_class {
    my $class = shift;

    my $ctx = context();

    my $obj = $class->new;
    is($obj->foo, 'foo', "got foo");
    is($obj->bar, 'bar', "got bar");

    $ctx->release;
}

See, that was easy. With these 2 additional lines we now have proper file+line reporting. The nested tools will find the context we acquired here, and know to use its file and line numbers. periodt. πŸ’…

πŸ’€ THE OLD WAY (DO NOT DO THIS ANYMORE) πŸ’€

With Test::Builder there was a global variable called $Test::Builder::Level which helped solve this problem:

sub check_class {
    my $class = shift;

    local $Test::Builder::Level = $Test::Builder::Level + 1;

    my $obj = $class->new;
    is($obj->foo, 'foo', "got foo");
    is($obj->bar, 'bar', "got bar");
}

This variable worked well enough (and will still work) but was not very discoverable. Another problem with this variable is that it becomes cumbersome if you have a more deeply nested code structure called the nested tools β€” you might need to count stack frames, and hope they never change due to a third party module. The context solution has no such caveats. πŸ‘‘

πŸ‘‘ SEE ALSO πŸ‘‘

  • Test2::Manual β€” Primary index of the manual.

🌈 SOURCE 🌈

The source code repository for Test2-Manual can be found at https://github.com/Test-More/test-more/.

πŸ’œ MAINTAINERS πŸ’œ

  • Chad Granum β€” <exodist@cpan.org>

🌸 AUTHORS 🌸

  • Chad Granum β€” <exodist@cpan.org>

Copyright Chad Granum <exodist@cpan.org>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://dev.perl.org/licenses/


okay that’s the whole page, bestie πŸ’– signed off by perl v5.42.3, dated 2026-11-03, and filed under Test2::...:Nesting(3pm). mwah πŸ«ΆπŸ’…

πŸ“¦ where this page comes from