Test::Stream(3)

πŸ“¦
version 1.302027 Β· 2 versions
, in fedora 43

✨ Test::Stream(3) ✨

User Contributed Perl Documentation β€” okay bestie, we're doing the Test::Stream(3) manual page, part 1 of 2. It's giving deprecated icon but still full of lore. πŸ’–


🏷️ NAME 🏷️

Test::Stream β€” DEPRECATED See Test2-Suite instead


πŸ’€ DEPRECATED πŸ’€

okay so real talk: this distribution is deprecated in favor of Test2, Test2::Suite, and Test2::Workflow. periodt.

See Test::Stream::Manual::ToTest2 for a conversion guide.


⚠️ READ THIS FIRST ⚠️

This is not a drop-in replacement for Test::More. no cap.

Adoption of Test::Stream instead of continuing to use Test::More is a choice. Liberty has been taken to make significant API changes. Replacing use Test::More; with use Test::Stream; will not work for more than the most trivial of test files.

See Test::Stream::Manual::FromTestBuilder if you are coming from Test::More or Test::Simple and want a quick translation.


🀝 COMBINING WITH OLD TOOLS 🀝

At the moment you cannot use Test::Stream and Test::Builder based tools in the same test scripts unless you install the TRIAL Test::More version. Once the Test::More trials go stable you will be able to combine tools from both frameworks.


πŸ“š MANUAL πŸ“š

The manual is still being written, but a couple pages are already available.

🧳 Migrating from Test::More

Test::Stream::Manual::FromTestBuilder

πŸ› οΈ How to write tools for Test::Stream

Test::Stream::Manual::Tooling

🧭 Overview of Test-Stream components

Test::Stream::Manual::Components


🌸 DESCRIPTION 🌸

This is the primary interface for loading Test::Stream based tools. This module is responsible for loading bundles and plugins for the tools you want. By default you are required to specify at least 1 plugin or bundle to load. You can subclass Test::Stream to have your own default plugins or bundles.

Bundles and plugins can be used directly, it is not necessary to use Test::Stream to load them. lowkey convenient, highkey iconic.


✨ SYNOPSIS ✨

use Test::Stream -Classic;

ok(1, "This is a pass");
ok(0, "This is a fail");

done_testing;

The '-' above means load the specified bundle, this is the same as:

use Test::Stream::Bundle::Classic;

ok(1, "This is a pass");
ok(0, "This is a fail");

done_testing;

🧬 SUBCLASS 🧬

package My::Loader;
use strict;
use warnings;

use parent 'Test::Stream';

# The 'default' sub just returns a list of import arguments to use byu
# default.
sub default {
    return qw{
        -Bundle1
        Plugin1
        ...
    };
}

1;

🚨 IMPORTANT NOTE 🚨

use Test::Stream; will fail. You MUST specify at least one bundle or plugin. If you do not specify any then none would be imported and that is obviously not what you want. If you are new to Test::Stream then you should probably start with one of the pre-made bundles:

  • -Classic - The Classic bundle. This one is probably your best bet when just starting out. This plugin closely resembles the functionality of Test::More.

See Test::Stream::Bundle::Classic.

  • -V1 - The bundle used in Test::Streams tests. This one provides a lot more than the Classic bundle, but is probably not suited to begginers. There are several notable differences from Test::More that can trip you up if you do not pay attention.

See Test::Stream::Bundle::V1.

πŸ€” WHY NOT MAKE A DEFAULT BUNDLE OR SET OF PLUGINS? πŸ€”

Future Proofing. If we decide in the future that a specific plugin or tool is harmful we would like to be able to remove it. Making a tool part of the default set will effectively make it unremovable as doing so would break compatability. Instead we have the bundle system, and a set of starter bundles, if a bundle proves ot be harmful we can change the recommendation of the docs.


🧩 PLUGINS, BUNDLES, AND OPTIONS 🧩

Test::Stream tools should be created as plugins. This is not enforced, nothing prevents you from writing Test::Stream tools that are not plugins. However writing your tool as a plugin will help your module to play well with other tools. Writing a plugin also makes it easier for you to create private or public bundles that reduce your boilerplate.

Bundles are very simple. At its core a bundle is simply a list of other bundles, plugins, and arguments to those plugins. Much like hash declaration a 'last wins' approach is used; if you load 2 bundles that share a plugin with different arguments, the last set of arguments wins.

Plugins and bundles can be distinguished easily:

use Test::Stream(
    '-Bundle',                      # Bundle ('-')
    ':Project',                     # Project specific bundle (':')
    'MyPlugin',                     # Plugin name (no prefix)
    '+Fully::Qualified::Plugin',    # (Plugin in unusual path)
    'SomePlugin' => ['arg1', ...],  # (Plugin with args)
    '!UnwantedPlugin',              # Do not load this plugin
    'WantEverything' => '*',        # Load the plugin with all options
    'option' => ...,                # Option to the loader (Test::Stream)
);

Explanation:

  • '-Bundle' β€” The - prefix indicates that the specified item is a bundle. Bundles live in the Test::Stream::Bundle:: namespace. Each bundle is an independant module. You can specify any number of bundles, or none at all.
  • ':Project' β€” The : prefix indicates we are loading a project specific bundle, which means the module must be located in t/lib/, lib/, or the paths provided in the TS_LB_PATH environment variable. In the case of ':Project' it will look for Test/Stream/Bundle/Project.pm in TS_LB_PATH, t/lib/, then lib/.

This is a good way to create bundles useful to your project, but not really worth putting on CPAN.

  • 'MyPlugin' β€” Arguments without a prefix are considered to be plugin names. Plugins are assumed to be in Test::Stream::Plugin::, which is prefixed automatically for you.
  • '+Fully::Qualified::Plugin' β€” If you write a plugin, but put it in a non-standard namespace, you can use the fully qualified plugin namespace prefixed by +. Apart from the namespace treatment there is no difference in how the plugin is loaded or used.
  • 'SomePlugin' => \@ARGS β€” Most plugins provide a fairly sane set of defaults when loaded. However some provide extras you need to request. When loading a plugin directly these would be the import arguments. If your plugin is followed by an arrayref the ref contents will be used as load arguments.

Bundles may also specify arguments for plugins. You can override the bundles arguments by specifying your own. In these cases last wins, arguments are never merged. If multiple bundles are loaded, and several specify arguments to the same plugin, the same rules apply.

use Test::Stream(
    '-BundleFoo',         # Arguments to 'Foo' get squashed by the next bundle
    '-BundleAlsoWithFoo', # Arguments to 'Foo' get squashed by the next line
    'Foo' => [...],       # These args win
);
  • '!UnwantedPlugin' β€” This will blacklist the plugin so that it will not be used. The blacklist will block the plugin regardless of where it is listed. The blacklist only effects the statement in which it appears; if you load Test::Stream twice, the blacklist will only apply to the load in which it appears. You cannot override the blacklist items.
  • 'WantEverything' => '*' β€” This will load the plugin with all options. The '*' gets turned into ['-all'] for you.
  • 'option' => ... β€” Uncapitalized options without a +, -, or : prefix are reserved for use by the loader. Loaders that subclass Test::Stream can add options of their own.

To define an option in your subclass simply add a sub opt_NAME() method. The method will receive several arguments:

sub opt_foo {
    my $class = shift;
    my %params = @_;

    my $list  = $params{list};  # List of remaining plugins/args
    my $args  = $params{args};  # Hashref of {plugin => \@args}
    my $order = $params{order}; # Plugins to load, in order
    my $skip  = $params{skip};  # Hashref of plugins to skip {plugin => $bool}

    # Pull our arguments off the list given at load time
    my $foos_arg = shift @$list;

    # Add the 'Foo' plugin to the list of plugins to load, unless it is
    # present in the $args hash in which case it is already in order.
    push @$order => 'Foo' unless $args{'Foo'};

    # Set the args for the plugin
    $args->{Foo} = [$foos_arg];

    $skip{Fox} = 1; # Make sure the Fox plugin never loads.
}

πŸŽ€ AVAILABLE OPTIONS πŸŽ€

  • class => $CLASS β€” Shortcut for the Test::Stream::Plugin::Class plugin.
  • skip_without => $MODULE
  • skip_without => 'v5.008'
  • skip_without => [$MODULE => $VERSION] β€” Shortcut for the Test::Stream::Plugin::SkipWithout plugin. Unlike normal specification of a plugin, this APPENDS arguments. This one can be called several time and the arguments will be appended.
Note: specifying 'SkipWithout' the normal way after a call to 'skip_without' will wipe out the argument that have accumulated so far.
  • srand => $SEED β€” Shortcut to set the random seed.

πŸ‘€ SEE ALSO πŸ‘€

For more about plugins and bundles see the following docs:

  • plugins β€” Test::Stream::Plugin - Provides tools to help write plugins.
  • bundles β€” Test::Stream::Bundle - Provides tools to help write bundles.

πŸ“œ EXPLANATION AND HISTORY πŸ“œ

Test::Stream has learned from Test::Builder. For a time it was common for people to write Test::* tools that bundled other Test::* tools with them when loaded. For a short time this seemed like a good idea. This was quickly seen to be a problem when people wanted to use features of multiple testing tools that both made incompatible assumptions about other modules you might want to load.

Test::Stream does not recreate this wild west approach to testing tools and bundles. Test::Stream recognises the benefits of bundles, but provides a much more sane approach. Bundles and Tools are kept separate, this way you can always use tools without being forced to adopt the authors ideal bundle.

🌈 ENVIRONMENT VARIABLES 🌈

okay bestie so this is the full list of env vars that Test::Stream actually looks at. like, it peeks at these. set them if you need them, ignore them if you don't πŸ’…


  • TS_FORMATTER="Foo"
  • TS_FORMATTER="+Foo::Bar"

this one sets your output formatter. no cap. by default it's Test::Stream::Formatter::TAP β€” that's the one you're getting if you don't say anything.

the way it works: normally Test::Stream::Formatter:: gets slapped onto the front of whatever value you put in there, like a little prefix bow πŸŽ€

$ TS_FORMATTER='TAP' perl test.t     # Use the Test::Stream::Formatter::TAP formatter
$ TS_FORMATTER='Foo' perl test.t     # Use the Test::Stream::Formatter::Foo formatter

but if you wanna name a full module name, whole thing, top to bottom, you hit it with the + prefix:

$ TS_FORMATTER='+Foo::Bar' perl test.t     # Use the Foo::Bar formatter

see? the + is giving "don't touch my name, it's already perfect" ✨

  • TS_KEEP_TEMPDIR=1

so some IPC drivers make temporary directories, right? well, this variable tells Test::Stream to keep that directory around after the tests finish instead of yeeting it into the void. lowkey useful when you're debugging and you're like "wait what was IN there" 😭

  • TS_LB_PATH="./:./lib/:..."

this gives you paths where Test::Stream will go searching for project-specific bundles. project-specific bundles, bestie. it goes looking.

⚠️ heads up though β€” these paths are NOT added to @INC. do not be fooled. they are for bundle-hunting and nothing else, okay? okay πŸ’–
  • TS_MAX_DELTA=25

this one is used by the Test::Stream::Plugin::Compare plugin. it says: when two data structures do NOT match, how many differences do you want to see max? this is the max number of differences to show. so 25 means "girl, just show me 25 of them, I cannot handle more" πŸ’€

  • TS_TERM_SIZE=80

this sets the width of your terminal. and it's used when Test::Stream is building up those tables of diagnostics. default is 80 β€” unless Term::ReadKey is installed, in which case the value gets determined dynamically, all on its own, main character energy, no input needed πŸ‘‘

  • TS_WORKFLOW=42
  • TS_WORKFLOW="foo"

used by the Test::Stream::Plugin::Spec plugin. this tells it which test block should be run. only the block you specify gets run. just that one. periodt βœ‹

  • TS_RAND_SEED=44523

this ONLY works when used with the Test::Stream::Plugin::SRand plugin. it lets you specify the random seed to use. so if you're chasing a flaky test and you're like "run it the exact same way again please" β€” this is her 🎲

  • HARNESS_ACTIVE

this is typically set by TAP::Harness and other harnesses. you should not need to set this yourself. hands off bestie, the harness handles it 🫢

  • HARNESS_IS_VERBOSE

same deal β€” typically set by TAP::Harness and other harnesses. you should not need to set this yourself. again: not yours to set 🌸

  • NO_TRACE_MASK=1

this variable is specified by Trace::Mask. Test::Stream uses the Trace::Mask specification to mask some stack frames from traces generated by Trace::Mask compliant tools. setting this variable will force a full stack trace whenever a trace is produced. full. every frame. no masking. buckle up πŸ”₯


πŸ“¦ SOURCE πŸ“¦

the source code repository for Test::Stream lives right here, and honestly go look at it, she's beautiful:

http://github.com/Test-More/Test-Stream/

πŸ‘‘ MAINTAINERS πŸ‘‘

  • Chad Granum <exodist@cpan.org>

πŸ’œ AUTHORS πŸ’œ

  • Chad Granum <exodist@cpan.org>

Copyright 2015 Chad Granum <exodist7@gmail.com>.

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, you made it, I'm so proud of you πŸ₯ΉπŸ’– this was written for perl v5.42.0, dated 2025-07-25, and it's the manual page for Test::Stream(3). go write some tests. slay. periodt. 🌈✨

πŸ“¦ where this page comes from