Giter Club home page Giter Club logo

test2-suite's Introduction

NAME

Test2 - Framework for writing test tools that all work together.

DESCRIPTION

Test2 is a new testing framework produced by forking Test::Builder, completely refactoring it, adding many new features and capabilities.

WHAT IS NEW?

  • Easier to test new testing tools.

    From the beginning Test2 was built with introspection capabilities. With Test::Builder it was difficult at best to capture test tool output for verification. Test2 Makes it easy with Test2::API::intercept().

  • Better diagnostics capabilities.

    Test2 uses an Test2::API::Context object to track filename, line number, and tool details. This object greatly simplifies tracking for where errors should be reported.

  • Event driven.

    Test2 based tools produce events which get passed through a processing system before being output by a formatter. This event system allows for rich plugin and extension support.

  • More complete API.

    Test::Builder only provided a handful of methods for generating lines of TAP. Test2 took inventory of everything people were doing with Test::Builder that required hacking it up. Test2 made public API functions for nearly all the desired functionality people didn't previously have.

  • Support for output other than TAP.

    Test::Builder assumed everything would end up as TAP. Test2 makes no such assumption. Test2 provides ways for you to specify alternative and custom formatters.

  • Subtest implementation is more sane.

    The Test::Builder implementation of subtests was certifiably insane. Test2 uses a stacked event hub system that greatly improves how subtests are implemented.

  • Support for threading/forking.

    Test2 support for forking and threading can be turned on using Test2::IPC. Once turned on threading and forking operate sanely and work as one would expect.

GETTING STARTED

If you are interested in writing tests using new tools then you should look at Test2::Suite. Test2::Suite is a separate cpan distribution that contains many tools implemented on Test2.

If you are interested in writing new tools you should take a look at Test2::API first.

NAMESPACE LAYOUT

This describes the namespace layout for the Test2 ecosystem. Not all the namespaces listed here are part of the Test2 distribution, some are implemented in Test2::Suite.

Test2::Tools::

This namespace is for sets of tools. Modules in this namespace should export tools like ok() and is(). Most things written for Test2 should go here. Modules in this namespace MUST NOT export subs from other tools. See the "Test2::Bundle::" namespace if you want to do that.

Test2::Plugin::

This namespace is for plugins. Plugins are modules that change or enhance the behavior of Test2. An example of a plugin is a module that sets the encoding to utf8 globally. Another example is a module that causes a bail-out event after the first test failure.

Test2::Bundle::

This namespace is for bundles of tools and plugins. Loading one of these may load multiple tools and plugins. Modules in this namespace should not implement tools directly. In general modules in this namespace should load tools and plugins, then re-export things into the consumers namespace.

Test2::Require::

This namespace is for modules that cause a test to be skipped when conditions do not allow it to run. Examples would be modules that skip the test on older perls, or when non-essential modules have not been installed.

Test2::Formatter::

Formatters live under this namespace. Test2::Formatter::TAP is the only formatter currently. It is acceptable for third party distributions to create new formatters under this namespace.

Test2::Event::

Events live under this namespace. It is considered acceptable for third party distributions to add new event types in this namespace.

Test2::Hub::

Hub subclasses (and some hub utility objects) live under this namespace. It is perfectly reasonable for third party distributions to add new hub subclasses in this namespace.

Test2::IPC::

The IPC subsystem lives in this namespace. There are not many good reasons to add anything to this namespace, with exception of IPC drivers.

Test2::IPC::Driver::

IPC drivers live in this namespace. It is fine to create new IPC drivers and to put them in this namespace.

Test2::Util::

This namespace is for general utilities used by testing tools. Please be considerate when adding new modules to this namespace.

Test2::API::

This is for Test2 API and related packages.

Test2::

The Test2:: namespace is intended for extensions and frameworks. Tools, Plugins, etc should not go directly into this namespace. However extensions that are used to build tools and plugins may go here.

In short: If the module exports anything that should be run directly by a test script it should probably NOT go directly into Test2::XXX.

SEE ALSO

Test2::API - Primary API functions.

Test2::API::Context - Detailed documentation of the context object.

Test2::IPC - The IPC system used for threading/fork support.

Test2::Formatter - Formatters such as TAP live here.

Test2::Event - Events live in this namespace.

Test2::Hub - All events eventually funnel through a hub. Custom hubs are how intercept() and run_subtest() are implemented.

CONTACTING US

Many Test2 developers and users lurk on irc://irc.perl.org/#perl-qa and irc://irc.perl.org/#toolchain. We also have a slack team that can be joined by anyone with an @cpan.org email address https://perl-test2.slack.com/ If you do not have an @cpan.org email you can ask for a slack invite by emailing Chad Granum [email protected].

SOURCE

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

MAINTAINERS

AUTHORS

COPYRIGHT

Copyright Chad Granum [email protected].

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

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

test2-suite's People

Contributors

2shortplanks avatar atoomic avatar autarch avatar colinnewell avatar craigberry avatar dakkar avatar dboehmer avatar djerius avatar eilara avatar exodist avatar gregoa avatar grinnz avatar haarg avatar jjatria avatar jonasbn avatar jonjensen avatar leonerd avatar mauke avatar michal-josef-spacek avatar nanto avatar petdance avatar plicease avatar schwern avatar spazm avatar stevieb9 avatar toddr avatar trwyant avatar wolfsage avatar yanick avatar yoshikazusawa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

test2-suite's Issues

'st', 'stc', 'stcs', 'sta', and 'stp' dsl subs

Instead of adding a ton of subs, with possible inconsistencies, we should define these 5 base dsl subs. They should be used consistently, and Compare::XXX types should know what to do with them (consistent!!!)

is(
    $structure,
    st Hash => sub {
        my $st = shift;
        # or
        my $st = st_this;

        stc key1 => 'val1';
        stc key2 => 'val2';

        stc nested => st Array => { sta empty => 1 };

        stc non_empty_string => st String => { sta empty => 0 };

        stc price => st Number => 12; 

        sta 'end'; # Or to be more explicit:
        sta end => 1; # or stp etc => 1,
    },  
    "name"
);

st - define a structure

st Type Val
st Type &Builder
to define a Test2::Compare::Type check

stc - structure component;

stc key => 'val' # like 'field'
stc idx => 'val' # for arrays (like item with an index)
stc 'val' # for arrays (like item with no idx)
stc $val # String/number/etc.
to define components inside the structure

stcs - structure values

stcs ...; # Specify several components at once

sta - structure attributes

sta 'attr' - toggle attribute on
sta attr => $val - set attribute to value
This is essentially calling a method on the check when used in a builder to toggle attributes

stp - structure property

stp what => val
This would be like a 'prop' to check properties of the thing you got.

Checking An Empty ArrayRef Against A bag Always Passes

It appears to be a problem with Test2::Compare::Bag::deltas but I couldn't devote further time to fixing it.

use Test2::Bundle::Extended;
use Test2::Tools::Compare qw( is bag item );

my $check = bag {
    item 'a';
    item 'b';
    end(); # Ensure no other elements exist.
};

is( [ ], $check, 'All of the elements from bag found!'); # passes but shouldn't

done_testing;

Test2::Plugin::Output2Events

Plugin that turns anything that is printed to the global STDOUT and STDERR into events. This will primarily be used by harnesses, but can also help with the order-mangling of printing to both STDERR and STDOUT.

test 4, t/modules/Util/Table.t fails if terminal columns < 80

The 4th test of t/modules/Util/Table.t fails iff my terminal has less than 80 columns. I've attached the output from make test TEST_FILE='t/modules/Util/Table.t'.

Looking at the end of term_size() the behaviour looks intentional, but it was surprising to me:

...
    return 80 if !$total;
    return 80 if $total < 80;
    return $total;
}

testoutput.txt

Test2::Compare does not load Test2::Compare::Array or ::Hash

#!/var/perl/bin/perl

use warnings;
use strict;

use Test2::Bundle::More;

is_deeply( { foo => 1 }, { bar => 2 } );
done_testing();

generates this

$ prove Dev/test2.t
[11:03:15] Dev/test2.t .. Can't locate object method "new" via package "Test2::Compare::Hash" (perhaps you forgot to load "Test2::Compare::Hash"?) at /var/perl5.20.3/lib/site_perl/5.20.3/Test2/Compare.pm line 87.
A context appears to have been destroyed without first calling release().
Based on $@ it does not look like an exception was thrown (this is not always
a reliable test)

This is a problem because the global error variables ($!, $@, and $?) will
not be restored. In addition some release callbacks will not work properly from
inside a DESTROY method.

Here are the context creation details, just in case a tool forgot to call
release():
  File: Dev/test2.t
  Line: 8
  Tool: Test2::Tools::ClassicCompare::is_deeply

Cleaning up the CONTEXT stack...
# No tests run!
# Looks like your test exited with 255 after test #0.
[11:03:15] Dev/test2.t .. Dubious, test returned 255 (wstat 65280, 0xff00)
No subtests run 
[11:03:15]

Test Summary Report
-------------------
Dev/test2.t (Wstat: 65280 Tests: 0 Failed: 0)
  Non-zero exit status: 255
  Parse errors: No plan found in TAP output
Files=1, Tests=0,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.05 cusr  0.00 csys =  0.07 CPU)
Result: FAIL

Similar results when doing array comparisons.

$ cat Dev/test2.t
#!/var/perl/bin/perl

use warnings;
use strict;

use Test2::Bundle::More;

is_deeply( [1], [2] );
done_testing();
$ prove Dev/test2.t
[11:04:16] Dev/test2.t .. Can't locate object method "new" via package "Test2::Compare::Array" (perhaps you forgot to load "Test2::Compare::Array"?) at /var/perl5.20.3/lib/site_perl/5.20.3/Test2/Compare.pm line 84.
A context appears to have been destroyed without first calling release().
Based on $@ it does not look like an exception was thrown (this is not always
a reliable test)

This is a problem because the global error variables ($!, $@, and $?) will
not be restored. In addition some release callbacks will not work properly from
inside a DESTROY method.

Here are the context creation details, just in case a tool forgot to call
release():
  File: Dev/test2.t
  Line: 8
  Tool: Test2::Tools::ClassicCompare::is_deeply

Cleaning up the CONTEXT stack...
# No tests run!
# Looks like your test exited with 255 after test #0.
[11:04:16] Dev/test2.t .. Dubious, test returned 255 (wstat 65280, 0xff00)
No subtests run 
[11:04:16]

Test Summary Report
-------------------
Dev/test2.t (Wstat: 65280 Tests: 0 Failed: 0)
  Non-zero exit status: 255
  Parse errors: No plan found in TAP output
Files=1, Tests=0,  0 wallclock secs ( 0.03 usr  0.00 sys +  0.04 cusr  0.01 csys =  0.08 CPU)
Result: FAIL

array, bag, and hash should add an implicit `end` when used with `is`.

Test2::Tools::Compare says right in the SYNOPSIS that is is "strict checking, everything must match".

    # Strict checking, everything must match
    is(
        $some_hash,
        {a => 1, b => 2, c => 3},
        "The hash we got matches our expectations"
    );

But with array, bag, hash that expectation doesn't hold true.

use Test2::Bundle::Extended;

my $array = [1,2,3,4];

is $array, array { item 1; item 2; item 3; end; }, "array w/end";          # fail
is $array, [1,2,3],                                "array ref";            # fail
is $array, array { item 1; item 2; item 3; },      "array wo/end";         # pass

is $array, bag { item 2; item 3; item 1; end; },   "bag w/end";            # fail
is $array, bag { item 2; item 3; item 1; },        "bag wo/end";           # pass


my $hash = { foo => 23, bar => 42, baz => 99 };

is $hash, { foo => 23, bar => 42 },                "hash ref";             # fail
is $hash, hash { field foo => 23; field bar => 42; end; }, "hash w/end";   # fail
is $hash, hash { field foo => 23; field bar => 42 },       "hash wo/end";  # pass

done_testing;
  • This is a trap for the user. It's unexpected for the writer and the reader. is is for equality. like is for matches. Having an is that does a match breaks that expectation.
  • The mistake is invisible. When the user inevitably forgets end the test will silently pass. They won't be aware of your mistake.
  • It breaks the expectation that [1,2,3] and array { item 1; item 2; item 3; } are equivalent.

It's caught me plenty of times, probably all my code that uses hash or array forgot to use end.

To avoid this, I would suggest that is add an implicit end to any comparison which can take it. This avoids a very common mistake, and it removes a very common exception to the behavior of is vs like.

As for backwards compatibility, I think we'll find that most folks are screwing this up too. It's early enough in Test2's lifespan that we can fix this before it spreads.

Add `etc`

As discussed in Slack and on #56, add etc. This explicitly states that an array or hash is not the complete specification regardless of being used by is or like.

Examples...

# After #56, both of these will be better written as `like`.
is [1,2,3], array { item 1; item 2; etc };
is { foo => 23, bar => 42 }, hash { field foo => 23; etc };

This opens the opportunity for etc to appear in other parts of an array.

# It contains 2 and 3 in that order
is [1,2,3,4,5], array { etc; item 2; item 3; etc; };

# It starts with 1 and ends with 5
is [1,2,3,4,5], array { item 1; etc; item 5; };

stp() DSL sub

This sub essentially replaces 'prop()' main difference is that 'this' is always an available property.

Use v1/v2 tagging for is+implicit end change

The current state of master applies the backcompat breaking change to all code. I think we need to use the planned v# export tag system instead, that is what it is there for!

  • Update Test2::Tools::Compare to use Importer
  • Current defaults, including old-style is() without implicit declare as ':v1' tag (and default when no tag specified)
  • ':v2' tag will have a new is() with implicit end, for now it will have extra diag when implicit end causes a failure to help people migrating, the diag will eventually go away.

Mixing streamed and buffered subtests hides the output from the streamed subtest

use strict;
use warnings;

use Test2::Bundle::Extended;
use Test2::Tools::Subtest;

subtest_buffered(
    'parent',
    sub {
        subtest_buffered(
            'buffered',
            sub {
                ok(1, 'b1');
                ok(1, 'b2');
            },
        );
        subtest_streamed(
            'streamed',
            sub {
                ok(1, 's1');
                ok(1, 's2');
            },
        );
    }
);

done_testing;

Outputs:

# Seeded srand with seed '20160917' from local date.
ok 1 - parent {
    ok 1 - buffered {
        ok 1 - b1
        ok 2 - b2
        1..2
    }
    # Subtest: streamed
    ok 2 - Subtest: streamed
    1..2
}
1..1

Note that using the subtest provided by Test::Builder has the same issue (and may require a separate fix).

st() DSL sub

st Type => ...

the Type must be in 'Test2::Compare::Check::Type' unless a '+' prefix is given to mean absolute path

The arguments after the first must either be a value (so if type is 'Array' it should be an arrayref) or a builder subref.

The first argument passed into the builder must be the check being constructed so that you can directly manipulate it if desired.

stcs() DSL sub

stc is pretty simple, pass all arguments to $check->add_components(...) for the check currently being built.

Please make plan() combatible with old-style plan()

All my subtests have plans.

subtest foo => sub {
    plan tests => 14;
}

In order to use Test2::Tools::Basic, I would have to change all of those to:

subtest foo => sub {
    plan 14;
}

If you'd allow either

plan tests => 14;
plan 14;

then it would save me from changing 1500 plans in 400 different files.

It would also be nice if we could use

use Test2::Bundle::Extended plan => 14;

just like we currently do

use Test::More plan => 14;

Add an all_items(CHECK) for arrays

Most of my code lives in checks. This works nicely, especially when validating deep hashes.

However, I've found that it's a bit awkward when it comes to arrays - currently, array checks can only be called manually. This doesn't work when you don't know how many items in an array before executing.

What I'd like, is something along the lines of:

my $is_below_ten = validator(sub {
  my (%params) = @_;

  return undef if $params{got} < 10;
});

is(
  [3, 4, 5],
  array {
    all_items $is_below_ten;
  }
);

Boolean context work breaks downstream

PR #113 broke Test2::Tools::EventDumper. Specifically it makes compare objects all false in boolean context, which is not right. I have reverted the PR until it can be corrected.

Test2::Tools::EventDumper caught the problem because it has a test where it constructs a compare object by first dumping what the code should be, eval'ing that code, and running conditionals, it considers the eval returning false to be a failure, since the compare objects all have boolean-false overloading all tests failed.

Test2::Compare may need to load Test2::Compare::{Hash,Array,etc...}

run this script

use Test2::Tools::ClassicCompare;# or use Test2::Bundle::More;
is_deeply({},{})

then I get module load error.

Can't locate object method "new" via package "Test2::Compare::Hash" (perhaps you forgot to load "Test2::Compare::Hash"?) at lib/perl5/Test2/Compare.pm line 88.
A context appears to have been destroyed without first calling release().
Based on $@ it does not look like an exception was thrown (this is not always
a reliable test)

This is a problem because the global error variables ($!, $@, and $?) will
not be restored. In addition some release callbacks will not work properly from
inside a DESTROY method.

Here are the context creation details, just in case a tool forgot to call
release():
  File: ./basic_sample.pl
  Line: 2
  Tool: Test2::Tools::ClassicCompare::is_deeply

However, Test2-Suite/t/modules/Tools/ClassicCompare.t is a success.
Because this test loads Test2::Bundle::Extended.
(
Test2::Bundle::Extended loads Test2::Tools::Compare. Test2::Tools::Compare loads Test2::Compare::Hash, Test::Compare::Array, and so on
)

Add a comparison function to Test2::Tools::Compare for two boolean values

I've got a function that I've been calling bool_eq that compares truthiness. Consider this example, probably in the middle of a big loop that goes over a bunch of different test cases.

my $thingy = find_a_thingy();  # Either returns a Thingy object or undef.
if ( $expect_to_find_a_thingy ) {  # Are we expecting to find one?
    ok( $thingy, 'Found a thingy' );
}
else {
    ok( !$thingy, 'Should not find a thingy' );
}

bool_eq lets that be rewritten as:

bool_eq( find_a_thingy(), $expect_to_find_a_thingy );

Or something like:

bool_eq( scalar @errors, $should_have_errors );

The diagnostics on failure would tell what values came in and how they evaluated:

#   Failed test 'Should have no errors'
#   at foo.t line 8.
#          got: true (5)
#     expected: false (0)
#   Failed test 'Should have found at least one user'
#   at foo.t line 10.
#          got: false (0)
#     expected: true (1)
#   Failed test 'Should not find a Mech object'
#   at foo.t line 9.
#          got: true (WWW::Mechanize=HASH(0x3cca328))
#     expected: false (0)

`meta` with Test2::Bundle::Extended has problems with Moose classes

When your test suite is a Moose class (for example, if you use Test::Class::Moose) then the exported meta function conflicts with both the inherited meta method and the meta that Moose exports, which causes problems:

package Hello;

use Test::Class::Moose bare => 1;
use Test2::Bundle::Extended;

sub test_reverse_dtrt {
    is(scalar(reverse('dlroW olleH')), 'Hello World', 'Hi');
}

__PACKAGE__->meta->make_immutable;
1;
Mark@travis:~/tmp/tcm$ prove -v runner.pl
runner.pl ..
# Seeded srand with seed '20160706' from local date.
Prototype mismatch: sub Hello/meta: none vs (&) at Hello.pm line 4.
Can't use string ("Hello") as a subroutine ref while "strict refs" in use at /opt/markperl/lib/site_perl/5.22.0/Test2/Compare.pm line 57.
 at /opt/markperl/lib/site_perl/5.22.0/Test2/Compare.pm line 59.
    Test2/Compare/build("Test2/Compare/Meta", "Hello") called at /opt/markperl/lib/site_perl/5.22.0/Test2/Tools/Compare.pm line 142
    Test2/Tools/Compare/meta("Hello") called at Hello.pm line 10
    require Hello.pm called at (eval 5) line 1
    Test/Class/Moose/Load/BEGIN() called at Hello.pm line 0
    eval {...} called at Hello.pm line 0
    eval 'use Hello ()' called at /opt/markperl/lib/site_perl/5.22.0/Test/Class/Moose/Load.pm line 54
    Test/Class/Moose/Load/_load("Test/Class/Moose/Load", "./Hello.pm", ".") called at /opt/markperl/lib/site_perl/5.22.0/Test/Class/Moose/Load.pm line 68
    Test/Class/Moose/Load/__ANON__() called at /opt/markperl/lib/5.22.0/File/Find.pm line 447
    File/Find/_find_dir(HASH(0x7fd55c99e718), ".", 4) called at /opt/markperl/lib/5.22.0/File/Find.pm line 235
    File/Find/_find_opt(HASH(0x7fd55c99e718), ".") called at /opt/markperl/lib/5.22.0/File/Find.pm line 759
    File/Find/find(HASH(0x7fd55c99e718), ".") called at /opt/markperl/lib/site_perl/5.22.0/Test/Class/Moose/Load.pm line 73
    Test/Class/Moose/Load/import("Test/Class/Moose/Load", ".") called at runner.pl line 6
    main/BEGIN() called at Hello.pm line 0
    eval {...} called at Hello.pm line 0
Compilation failed in require at (eval 5) line 1.
BEGIN failed--compilation aborted at (eval 5) line 1.
BEGIN failed--compilation aborted at runner.pl line 6.
# No tests run!
# Looks like your test exited with 255 after test #0.
Dubious, test returned 255 (wstat 65280, 0xff00)
No subtests run

Test Summary Report
-------------------
runner.pl (Wstat: 65280 Tests: 0 Failed: 0)
  Non-zero exit status: 255
  Parse errors: No plan found in TAP output
Files=1, Tests=0,  1 wallclock secs ( 0.01 usr  0.01 sys +  0.26 cusr  0.03 csys =  0.31 CPU)
Result: FAIL

Allow `bag` to take an array ref.

is $set, bag { item 1; item 2; item 3; end; };   # annoying
is $set, bag [1,2,3];  # easy

is [sort @thing], [sort @thing];  # annoying
is \@thing, bag \@thing;  # easy

Test2::Plugin::UTF8 globally setting a utf8 layer on STDOUT/STDERR is questionable

Test2::Plugin::UTF8, among other things, sets a :utf8 layer on the global STDOUT and STDERR handles. While it will usually be used at a top level in a test file via Test2::Bundle::Extended or similar, it still doesn't seem like the most compatible idea. For example, if a test file uses Test2::Bundle::Extended as well as a library which is using Test::More (or another Test::Builder based tool), that library will assume that its test output needs to be encoded to UTF-8, resulting in double-encoded output. There may also be other failure cases I'm not thinking of, due to the global nature of the handles.

I don't know enough about the architecture to know the feasibility, but would it be possible to instead have Test2::Plugin::UTF8 set something internally in Test2 which then causes any output to be encoded before being passed to STDOUT/STDERR? Or is there another solution that would work without affecting the global handles?

The Class tools do not allow test description

None pof isa_ok, can_ok, or DOES_ok allow a test description. This makes for a weird and easily forgettable API, where they are the outliers among all the test functions.

This wasn't a good idea for Test::More, and I'd hate to see Test2::Suite repeat that mistake. I think it'd be preferable to accept a single class or arrayref of classes (or methods, etc) and allow the third argument to be the test description.

Forward Pathing

Back when I was first creating Test2::Suite I had a vision for how it could move forward when breaking changes were needed. I mostly forgot, and even started to move away from that path... I think I need to return to the original plan, and I will document it here as best I can.

With Test2::Suite tools, plugins, and bundles are all treated as disposable. This is why tools and plugins are separated out by functionality, and are very small units. With exception of Test2::Tools::Compare nearly everything in Test2::Tools::* is very tiny.

The idea was that if Test2::Tools::Foo turned out to have serious problems, or be a bad idea, we could replace it by writing Test2::Tools::Bar and recommend people stop using ::Foo. The consequences are minimal, people already using ::Foo don't break, and people writing new tests can use ::Bar instead.

Bundles are also supposed to work this way, if a bundle includes a bunch of bad tools then simply write a new bundle, it should be easy since bundles have minimal logic and simply re-export tools and plugins.

Right now there are 2 modules in particular that we are having trouble with:

  • Test2::Tools::Compare
  • Test2::Bundle::Extended

The reason we are having trouble with Test2::Tools::Compare is because it really did break away from the small and disposable model. The extended bundle has a problem because the compare tools have a problem, but also because it is the "recommended" bundle, so replacing it is strange.


One of the things I as working on to try to address moving forward was the use of '+v#' pins. Another idea that is similar, but less crazy and more maintainable is the use of Foo::V# variants, like what perl5i uses.

I have a problem with this system. This works with perl5i because it is a single module the consumer uses. Test2::Suite is a ton of modules, and the consumer can use any number/combination of them. Having V variants of each requires a lot of overhead for the consumers. Even if we make it so that they all get a new variant at once with the same number it is still a lot of cognitive overhead.

I don't like either system here for most things, though I do still want to adopt the perl5i ::V# system for one specific thing: Recommended bundle.

I think we should create Test2::Bundle::V and Test2::Bundle::V1. Stop reommending 'Extended' and start recommending Test2::Bundle::V. Loading bundle V will load the latest V# variant, but warn that the user should use V# (whatever is latest) instead. If someone uses V1 and we create V2 they will not break because we will not change V1 in a breaking way once it is created.


What about Test2::Tools::Compare?

Boils down to this: I think that including the compare functions is, like, etc and the dsl in a single tools module is/was a mistake.

  • The dsl functions should be split into a new Tools::* module (maybe Test2::Tools::Compare::DSL).
  • We should deprecate (via a warning) the import of the dsl functions from Test2::Tools::Compare (but not break them!)
  • The new DSL tools should be written in the new styles have been discussing, breaking compatability here is fine because it is a new package, people have to choose to switch
  • The V1 bundle will export the new tools, the 'Extended' bundle will continue to provide the old.

What about is() and implicit end?

The more I think about it, the more I think not having an implicit end() was a bug, plain and simple. I suspect I was the only one who knew, and intentionally used the lack of implicit end as a feature. So lets just fix the damn bug. Things that break from fixing this bug break because a bug was letting them pass when they should not have. And the fix is easy, add an etc call to the structure.

That said, I also think we should give extra information when a test fails due to implicit end() to explain the situation better (this previously passed, but fails now, here is why....)

Since this is the only change to the non-dsl tools in Test2::Tools::Compare we will not need to replace Test2::Tools::Compare or create and variants for it.


So to summarize:

  • Fix the implicit end bug
  • Write a DSL tool module with our new DSL ideas (still in the works)
  • Deprecate the dsl tools in Test2::Tools::Compare
  • Recommended bundle is now Test2::Bundle::V and numeric variants as we need to make breaking changes.

cmp_ok() with non-integer numbers gives incorrect diagnostic output

Call cmp_ok with integers and all is good.

$ perl -MTest2::Tools::ClassicCompare -E'cmp_ok( 1, ">", 4 )'
not ok 1
# Failed test at -e line 1.
# +-----+----+-------+
# | GOT | OP | CHECK |
# +-----+----+-------+
# | 1   | >  | 4     |
# +-----+----+-------+

But call it with decimals and you get this:

$ perl -MTest2::Tools::ClassicCompare -E'cmp_ok( 1.23456, ">", 4.5678 )'
not ok 1
# Failed test at -e line 1.
# +------+---------+----+--------+
# | TYPE | GOT     | OP | CHECK  |
# +------+---------+----+--------+
# | num  | 1       | >  | 4      |
# | orig | 1.23456 |    | 4.5678 |
# +------+---------+----+--------+

stc() DSL sub

stc is pretty simple, it takes exactly 1 or 2 arguments, and passes both to $check->add_component(...) for the check currently being built.

sta() DSL sub

sta 'method' and sta method => $val

Call 'method' on the check being constructed. If no second argument is given then use '1' as the argument. Otherwise use the specified argument.

Only call methods listed in a hash returned by the 'dsl_attributes' class method on the check, calling an unlisted method is an exception.

The Exception tools should provide something like lives_ok

If I expect something to live, I really want to get diagnostic of the exception when it doesn't. The lives or note idiom is really suboptimal.

Even if you don't want to add this, I'd at least update the docs to suggest something like is( exception { ... }, undef, ... )

Create a "how to work on Test2::Suite" document

Start with explaining that you have to use Dist::Zilla, not EU::MM, and how to use Dist::Zilla to do common tasks. Not everyone uses it, and without a Makefile.PL, someone unfamiliar with Dist::Zilla will not understand what to do.

Should we deprecate using tools/bundles without pins?

I am working on updating all Test2::Suite packages that export symbols. All of them will be updated to use an upcoming feature in Importer called 'pins'. A pin is a way to version exports from a module, the main example is Compre:

use Test2::Tools::Compare ':v1';

vs

use Test2::Tools::Compare ':v2';

The first will import is() that does not automatically add bounds-checking to checks built with the dsl. The second one will import the updated is() that does bound checking. providing no pin will default to ':v1' for back-compat, don't want to break things.

Question is, should we add a warning whenever someone loads a bundle/tool without specifying a pin? I think we should, that way everyone will need to add :v1 or :v2, and will be forced to know about pins. This way they can also always check if there is a newer pin that provides updated features.

I do not think we should warn when people are using older pins, that almost defeats their purpose, though I think an env var to turn on such warnings would be useful.

I am leaning towards warning on no-pin with bundles, I am not so sure on tools:: packages..

DNE() with in_set() fails

Maybe I'm doing something wrong, but I would have expected that all of these tests should pass:

#!/usr/bin/perl

use strict;
use warnings;

use Test2::Bundle::Extended;

my $check = hash {
  field first   => 42;
  field second  => undef;
  field third   => DNE();
  field fourth  => in_set(42, undef);
  field fifth   => in_set(42, undef);
  field sixth   => in_set(42, DNE());
  field seventh => in_set(42, DNE());
};

is(
  {
    first   => 42,
    second  => undef,
    # third DNE
    fourth  => 42,
    fifth   => undef,
    sixth   => 42,
    # seventh DNE <- fails :(
  },
  $check
);

Please add prototypes to the comparison functions

Function prototypes are a huge benefit when writing tests, because it's so easy to get the arguments goofed up. You're thinking cmp_ok when writing is, and you do this:

is( 'dog', '==', 'cat', 'Are dogs the same as cats?' );

Under Test2, the test will fail but the code will execute.

Using Test::More I would get:

Too many arguments for Test::More::is at foo.pl line 7, near "'Are dogs the same as cats?' )"
Execution of foo.pl aborted due to compilation errors.

Anything we can do to catch errors at compile time is a big win.

string() confesses when not defined

The docs say:

Note: This will fail if the recieved value is undefined, it must be defined.

So when using string() as a quick check, it confesses when not defined. Shouldn't it simply return false if it is not a string?

Make the difference between `is` and `like` easier to understand.

#56 and #62 bring up interesting questions about the behavior of is and like. The docs say is is the "strict checker" and like is the "relaxed checker". And I've been thinking of them this way. is is an equality check and like says we expect it to contain the selected things.

But upon reading the docs there's a lot more than that and some of them are quite surprising.

use Test2::Bundle::Extended;

use v5.10;

# This is "foo" =~ qr/foo/
like "foo", qr/foo/, "like regex";               # pass

# This is "foo" eq qr/foo/ ... or something?
is "foo",   qr/foo/,   "is regex";               # fails

# This uses the code ref to check 'foo'
like "foo", sub { $_ eq 'foo' }, 'like coderef'; # pass

# This checks if "foo" is the code ref?
is   "foo", sub { $_ eq 'foo' }, 'is coderef';   # fails

done_testing;

These is features are for extremely rare cases. They complicate understanding is vs like. The like ones are pretty useful, being able to check strings with regexes and evaluating complicated nested conditions with code refs. It sucks that you can only get them with like. You can't choose "strict checking but also let me use regexes and code".

I propose...

  • Make is and like behave as similar as possible
    • Eliminate the code ref and regex and any other special cases
    • Replace them with coderef and regex DSLs (see below).
  • is checks that the two sides are the same
  • like checks that the LHS contains the RHS

The special case behavior of is is replaced by adding to the DSL. Then you can use it with like if you want.

is $code, codref $some_code_ref;  # check that $code is $some_code_ref
is $regex, regex qr{foo};  # check $regex is the same as qr{foo}

Yes, this does call into question what is really the difference between is and like.

Yes, I'm increasingly thinking of Test2 as a DSL rather than a collection of comparison functions.

Feature request: U()

The D() quick check is useful, but I find myself wanting the opposite - U(). As it doesn't exist yet, the following works but is a bit tedious:

not_in_set(D())

Please add release tests running Test::Pod and Test::Spelling

lintian (Debian's package checker) says about Test2::Suite:

I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Bundle::More.3pm.gz seperate separate
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Bundle::More.3pm.gz compatability compatibility
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Bundle::Simple.3pm.gz seperate separate
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Compare::Base.3pm.gz overriden overridden
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Compare::Number.3pm.gz recieved received
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Compare::String.3pm.gz recieved received
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Mock.3pm.gz overriden overridden
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Mock.3pm.gz overriden overridden
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Mock.3pm.gz orignal original
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Plugin::UTF8.3pm.gz wether whether
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Require.3pm.gz recieve receive
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Require::Fork.3pm.gz mimick mimic
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Suite.3pm.gz compatability compatibility
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools.3pm.gz seperate separate
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::Basic.3pm.gz seperator separator
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::Basic.3pm.gz seperator separator
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::ClassicCompare.3pm.gz isnt isn't
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::Compare.3pm.gz isnt isn't
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::Compare.3pm.gz specfied specified
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Tools::Mock.3pm.gz existance existence
I: libtest2-suite-perl: spelling-error-in-manpage usr/share/man/man3/Test2::Util::Table.3pm.gz anomolies anomalies

W: libtest2-suite-perl: manpage-has-errors-from-pod2man usr/share/man/man3/Test2::Util::Grabber.3pm.gz:218

the latter being (perldoc output)

POD ERRORS
    Hey! The above document had some coding errors, which are explained
    below:

    Around line 125:
        '=item' outside of any '=over'

    Around line 163:
        You forgot a '=back' before '=head1'

Maybe someone (@karenetheridge?) can add author tests like in Test::Simple's xt/author/ here as well?

Thanks,
gregor, Debian Perl Team

Create a check "nonblank"

In the old system, I have many tests that are of the form

is_nonblank( $foo->{name} );

which is effectively

ok( !ref($foo->{name}) && length($foo->{name}) > 0 );

And we should be able to do

is( $foo, hash { field name => nonblank } );

Orphaned dependency on List::Util

Test2-Suite-0.000065 moved a code into a separate Term-Table distribution, but forgot to remove a dependency on List::Util module (Makefile.PL:23) that is not used in Test2-Suite anymore.

I have written some more comparisons

First of all, thank you for the herculean work you've done. Test2 is much nicer to work with than Test::Builder could ever be.

Now, I found myself in need of the functionality of Test::Deep::bag, and I couldn't find something equivalent for Test2. So I wrote it: Test2::Compare::Bag, Test2::Tools::MoreCompare.

The latter also provides a call_list which behaves like call but evaluates the method in list context. list_call $method => $result is a shortcut for call(sub{[$_->$method]},$result,$method)

Should I release those packages to CPAN, or should I provide them here as a pull request? If the former, does anyone have a better name than Test2::Tools::MoreCompare?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.