Test::Dist::Zilla - Test your Dist::Zilla plugin


Version v0.4.4, released on 2016-12-28 19:48 UTC.


    package Test::Dist::Zilla::Build;

    use namespace::autoclean;
    use Test::Routine;
    use Test::Deep qw{ cmp_deeply };

    with 'Test::Dist::Zilla';

    test 'Build' => sub {
        my ( $self ) = @_;
        my $expected = $self->expected;
        cmp_deeply( $self->exception, $expected->{ exception } );
        if ( exists( $expected->{ messages } ) ) {
            cmp_deeply( $self->messages, $expected->{ messages } );



This is a Test::Routine-based role. It does not provide any test routines, but it establishes infrastructure for writing tests on Dist::Zilla and its plugins. A test written with Test::Dist::Zila does not require external source files (which are usually placed into corpus/ directory) — all the source files (including dist.ini) for the test are generated on-the-fly in a temporary directory.

The role is not intended to be used directly in tests. Instead, it serves as a base for other more specific roles, for example, Test::Dist::Zilla::Build.



Hash of distribution options: name, version abstract, etc. to write to the test's dist.ini. This attribute is passed to dist_ini as \%root_config argument, see "dist_ini" in Test::DZil.

HashRef. Default value can be overridden by defining _build_dist builder.


    sub _build_dist { {
        name     => 'Assa',
        version  => '0.007',
        author   => 'John Doe',
    } };

    run_me {
        dist => {
            name     => 'Shooba',
            version  => 'v0.7.0',
            author   => 'John Doe, Jr.',

TODO: Merge specified keys into default?


Plugin configuration to write to the test's dist.ini. Attribute is passed to dist_ini as @plugins argument, see "dist_ini" in Test::DZil.

ArrayRef, optional. Default value is empty array (i. e. no plugins), it can be overridden by defining _build_plugins builder.


    sub _build_plugin { [
    ] };

    run_me {
        plugins => [
            [ 'PodWeaver' => {
                'replacer' => 'replace_with_comment',
            } ],


Hash of source files to add to the test's distribution source. Keys are file names, values are file contents. A file content may be specified by a (possibly multi-line) string or by array of lines (newlines are optional and will be appended if missed).

Note: Explicitly specified dist.ini file overrides dist and plugins attributes.

HashRef, optional, default value is empty hash (i. e. no files).


    sub _build_files { {
        'lib/' => [
            'package Assa;',
            '# VERSION',
        'Changes'  => "Release history for Dist-Zilla-Plugin-Assa\n\n",
        'MANIFEST' => [ qw{ lib/ Changes MANIFEST } ],
    } };

    run_me {
        files => {
            'lib/Assa.pod' => [ ... ],


Test-enabled Dist::Zilla instance (or DieHard "survivor" object, if Dist::Zilla constructing fails).

By default Dist::Zilla instance is created by calling Builder->from_config( ... ) with appropriate arguments. Thanks to Dist::Zilla::Tester::DieHard, it is never dies even if constructing fails, so $self->tzil->log_message returns the log messages anyway.

Note: Avoid calling build and release on tzil:

    $self->tzil->build();       # NOT recommended
    $self->tzil->release();     # NOT recommended

Call build and release directly on $self instead:

    $self->build();             # recommended
    $self->release();           # recommended

See build and release method descriptions for difference.


    use Path::Tiny;
    test 'Check META.json' => sub {
        my ( $self ) = @_;
        my $built_in = path( $self->tzil->built_in );
        my $json = $built_in->child( 'META.json' )->slurp_utf8;
        cmp_deeply( $json, $self->expected->{ json } );


Exception occurred, or undef is no exception was occurred.

    test 'Post-build' => sub {
        my ( $self ) = @_;
        cmp_deeply( $self->exception, $self->expected->{ exception } );


A hash of expected outcomes. Test::Dist::Zilla itself does not use this attribute, but more specific roles may do. For example, Test::Dizt::Zilla::Build uses exception and messages keys, Test::Dizt::Zilla::BuiltFiles uses files key.

HashRef, required.


    run_me {
        expected => {
            exception => "Aborting...\n",
            messages  => [
                '[Plugin] Oops, something goes wrong...',


If message_filter is defined, it is used by default messages implementation to filter the actual log messages. message_filter function is called once with list of all the log messages. The function is expected to return a list of messages (possibly, grepped and/or edited).

Note: message_filter value is a function, not method — messages method does not pass $self reference to the message_filter.

If messages method is overridden, the attribute may be used or ignored — it depends on new messages implementation.

Maybe[CodeRef], optional. There is no default message filter — messages method returns all the messages intact. Default message filter may be set by defining _build_message_filter builder.


Pass messages only from Manifest plugin and filter out all other messages:

    sub _build_message_filter {
        sub { grep( { $_ =~ m{^\[Manifest\] } ) @_ ) };

Drop plugin names from messages:

    run_me {
        message_filter => sub { map( { $_ =~ s{^\[.*?\] }{}r ) @_ ) },




The methods call same-name method on tzil, catch exception if any thrown, and save the caught exception in the exception attribute for further analysis.

Avoid calling these methods on tzil — some tests may rely on method modifiers, which are applicable to $self->method() but not to $self->tzil->method().


    test Build => sub {
        my ( $self ) = @_;
        $self->build();     # == dzil build

    test Release => sub {
        my ( $self ) = @_;
        $self->release();   # == dzil release


This method is assumed to return ArrayRef of Dist::Zilla log messages. It may be complete log as it is or not — the method may filter out and/or edit actual messages to make them more suitable for comparing with expected messages.

Default implementation filters the actual messages with the message_filter (if it is defined). If default behaviour is not suitable, the method can be overridden.


    cmp_deeply( $self->messages, $self->expected->{ messages } );


This convenience method makes test routines a bit shorter. Instead of writing

    if ( defined( $self->exception ) ) {
        plan skip_all => 'exception occurred';

you can write just




Dist::Zilla::Tester creates distribution source and build directories in a temporary directory, and then unconditionally cleans it up. Sometimes (especially in case of test failure) such behavior may be not desirable — you may want to look at source or built files for troubleshooting purposes.

Test::Dist::Zilla provides control over temporary directory via $Test::Dist::Zilla::Cleanup package variable:


Never clean up temporary directory.


Clean up temporary directory only if the test is passed (it is default).


Always clean up temporary directory.

If temporary directory is going to remain, the test output will contain diagnostic message like this one:

    # tempdir: tmp/AKrvBhhM4M

to help you identify the temporary directory created for the test.

Note: Controlling temporary directory requires Dist::Zilla 5.021 or newer.


"dist_ini" in Test::DZil


Van de Bugger <>


Copyright (C) 2015, 2016 Van de Bugger

License GPLv3+: The GNU General Public License version 3 or later <>.

This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.