http://www.perlmonks.org?node_id=1029098

space_monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear fellow Monks,

I am trying to be good this time and write unit tests using Test::More. A number of my methods return XML and I wish to write a subtest that verifies the XML data and which can be called a number of times with different parameters.

How do I pass parameters to the subtest? Updates and progress is below:

Best solution (so far): seems to be to use a wrapper method containing an anonymous subtest routine, passing the parameters to the wrapper.

sub my_wrapper { my ($name, $xml) = @_; subtest 'My subtest' => sub { like( $xml, qr/xml version=/, "$name: XML Document"); }; } my $m1Xml = MyPackage::method1(); my_wrapper( 'Method 1 XML', $m1Xml); my $m2Xml = MyPackage::method2(); my_wrapper( 'Method 2 XML', $m2Xml);
If any of my proposed solutions have minor errors, it's because I don't waste my genius on trivial matters. :-P

Replies are listed 'Best First'.
Re: Test::More subtest parameters
by Khen1950fx (Canon) on Apr 17, 2013 at 10:39 UTC
    You can pass parameters like this, borrowing some fudge from Anonymous Monk:
    #!/usr/bin/perl use strict; use warnings; use Test::More tests => 3; pass("This is fudge"); subtest 'Here comes the fudge' => sub { pass("This is white chocolate fudge"); pass("This is dark chocolate fudge"); }; pass("Fudge is fattening"); done_testing();
    Subtests can be further divided into subsubtests. They're nestable. Note that a subtest and all of its subtests count as one test.

      Thanks for the reply...

      Your subtest is simply an anonymous routine which does not take any parameters. To properly illustrate what I wanted perhaps the code below will clarify....

      my $xml; sub my_subtest { like( $xml, qr/xml version=/, "XML Document"); # more tests on XML document text here.... } $xml = MyPackage::method1(); subtest 'Checking XML from method1' => \&my_subtest; $xml = MyPackage::method2(); subtest 'Checking XML from method2...' => \&my_subtest;
      I would like to pass $xml as a variable (plus other test parameters) to the subtest. I think the original fudge poster was close as it looks as though having a "fudge wrapper" is the only way this can be achieved. I did wonder if there was a more direct method or whether subtest passed on additional params to "my_subtest".
      If any of my proposed solutions have minor errors, it's because I don't waste my genius on trivial matters. :-P
        Anonymous subs to the rescue:
        #!/usr/bin/perl use warnings; use strict; { package MyPackage; sub method1 { '<?xml version="1.0"?>' } sub method2 { '[?xml version="2.0"?][a][/a]' } } use Test::More; sub my_subtest { like( shift, qr/xml version=/, "XML Document"); # more tests on XML document text here.... } for my $method (qw/method1 method2/) { subtest "Checking $method" => sub { my_subtest(MyPackage->$method) + }; }
        Update: Unfortunately, I am not able to use a sub returning a sub as in
        sub new_test { my ($test, $method) = @_; return sub { $test->(MyPackage->$method) }; } for my $method (qw/method1 method2/) { subtest "Checking $method", new_test(\&my_subtest, $method); }

        I am getting

        Type of arg 2 to Test::More::subtest must be sub {} (not subroutine en +try)
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Test::More subtest parameters
by kcott (Archbishop) on Apr 17, 2013 at 16:51 UTC

    G'day space_monk,

    I tried a few tests with callback formats I'd come across elsewhere (e.g. [\&function, @args] from Perl/Tk) but subtest emits a warning that the 2nd argument must be a coderef. Given this, your best bet might be something like:

    subtest 'test name' => sub { my_subtest($xml, @other_args) };

    I played around with this for a bit and it seems to work well. I was able to conditionally run subtest tests based on the arguments passed in: I don't know if that's exactly what you want to do with it but it certainly "can be called a number of times with different parameters".

    #!/usr/bin/env perl use strict; use warnings; use Test::More tests => 9; my $xml = '<tag>content</tag>'; pass('PRE subtest'); subtest 'xml_subtest(undef)' => sub { xml_subtest(undef) }; subtest 'xml_subtest("blah")' => sub { xml_subtest("blah") }; subtest 'xml_subtest("blah", undef)' => sub { xml_subtest("blah", undef) }; subtest 'xml_subtest("blah", {})' => sub { xml_subtest("blah", {}) }; subtest 'xml_subtest($xml, {test_format => 1})' => sub { xml_subtest($xml, {test_format => 1}) }; subtest 'xml_subtest($xml, {test_tag => 1})' => sub { xml_subtest($xml, {test_tag => 1}) }; subtest 'xml_subtest($xml, {test_content => 1})' => sub { xml_subtest($xml, {test_content => 1}) }; pass('POST subtest'); sub xml_subtest { my ($sub_xml, $args) = @_; plan tests => 5; ok(defined $sub_xml, '$sub_xml defined'); ok(length $sub_xml, '$sub_xml populated'); ok(defined $args, '$args defined'); ok(ref $args eq 'HASH', '$args is hashref'); if ($args->{test_format}) { like($sub_xml, qr{^<([a-z]+)>[^<]+</\1>$}, '$sub_xml well-formed'); } elsif ($args->{test_tag}) { is(($sub_xml =~ m{^<([a-z]+)>})[0], 'tag', '$sub_xml tag element'); } elsif ($args->{test_content}) { cmp_ok(($sub_xml =~ m{>([^<]+)<})[0], 'eq', 'content', '$sub_xml tag content'); } else { fail('No tests indicated'); } }

    Test results:

    -- Ken

Re: Test::More subtest parameters
by Anonymous Monk on Apr 17, 2013 at 09:14 UTC

    How do I pass parameters to the subtest?

    I kinda think, if you think you need to do that, you're doing it wrong -- but you could do it

    sub fudge { my( $name , $cb , $asbestos, @rest ) = @_; subtest $name, sub { $cb->($asbestos, @rest ); }; }

    ?Maybe (probably not) subtest itself has a syntax for doing this  subtest $name, $callback, @args; ?

    FWIW, I've never seen subtest in the wild, and I've seen a lot of tests

      I do something fairly similar to that here.

      I use subtest from time to time, but not very often.

      package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name