Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Re^4: Introspecting function signatures

by szabgab (Priest)
on Mar 06, 2021 at 13:57 UTC ( [id://11129188] : note . print w/replies, xml ) Need Help??

in reply to Re^3: Introspecting function signatures
in thread Introspecting function signatures

It is an experiment to write a module to allow you to write tests like this:
sub test_someting($tempdir) { ... }
The test harness runs all the individual test_something functions. If the function has arguments that it will prepare the appropriate object and call the function passing those objects into the function. The example I created was injecting a $tempdir object, but, once the system is ready, you could write and register your own objects. In Pythest these are called fixtures, and they mostly act test fixtures. (In Pytest there are a few other ready-made such fixtures, for example one that captures stdout/stderr, and one that can mock methods, attributes, etc.)

If you look at the modules in the t/ directory, those are all examples on how to use the module to write tests.

Replies are listed 'Best First'.
Re^5: Introspecting function signatures
by Corion (Patriarch) on Mar 06, 2021 at 14:51 UTC

    Is there a reason why they need to be available as parameters at all? These fixtures could just be global variables, couldn't they?

      They could, but this way they are localized to the function, they go out of scope after the function is done and one test will not impact another test.

        I would use local for that:

        { local $tempdir = '/tmp/foo'; test_someting(); }

        Most likely you need to wrap all these test_something(...) function calls anyway with your code, and that's a good place to set up the localized test fixtures as well.

        I'm wondering a bit how you plan to handle one such function calling another function. Should the fixtures be recreated or not?

        sub test_someting2( $tempdir, $aValue ) { if( $^O !~ /mswin/i ) { test_someting("$tempdir/$aValue"); } else { test_someting("$tempdir\\$aValue"); }; }
Re^5: Introspecting function signatures
by LanX (Saint) on Mar 06, 2021 at 15:09 UTC
    I may have trouble understanding the full intention.... please help me:

    You want to parse

    sub test_someting($tempdir) { # do some tests }

    And because you see the (registered) keyword "tempdir" your test system is supposed to call this test with a prepared temporary directory?

    ok( test_something( create_tempdir() ), "something", )

    Kind of a naming convention?

    Did I get it right?

    If yes, I could think of some alternative (and more perlish) ways to achieve this without needing to parse the signature.

    PS: not sure why you call this "dependency injection" or do you plan testruns where the injected $tempdir is not a temporary dir? :)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Yes. "dependency injection" is how this is called in the case of pytest, maybe it is not a generic term. You could create your own "fixture" and then you or others could set its name as a parameter.

      I put together an example of creating and registering a fixture and then also use that fixture.

      I would love to see your suggestion for more Perlish way.

        My suggestion is to just have the test routine call back into the framework:

        sub test_something { my $tempdir = create_tempdir(); # ... }

        Then the framework only needs to iterate over the package stash and call all test_* functions. Which leads to a "is this a good idea?" question — this style of testing means that the test routines are included with the main code and will have to be compiled every time the module is loaded. Python reduces this overhead by automatically using its form of B::Bytecode/ByteLoader but Perl does not do that, so this style will add some incremental overhead to all use of any module using it. The traditional Perl style of separate test scripts completely avoids this last problem.