in reply to Re^2: How to write testable command line script?
in thread How to write testable command line script?

First up: never just add stuff to suppress warnings or errors. Figure out what the cause is and fix the cause!

In this case the warning was telling you something you needed to know. Lets boil the code down a little to demonstrate the issue:

#!/usr/bin/env/perl use strict; use warnings; reduce(1); sub reduce { my @array = @_; print $array[-2]; }

Prints:

Use of uninitialized value in print at ...\noname1.pl line 10.

Changing the call to reduce(1, 2); works as expected. The actual problem is you are passing only one argument and using that to populate the array. You then try to access the array expecting two arguments.

Note that much of your code is fairly old school. In particular don't use & to call functions - it doesn't do what you expect. Also, don't use @array[$idx]. If you want a single element use $array[$id] - $ instead of @.

Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

Replies are listed 'Best First'.
Re^4: How to write testable command line script?
by thechartist (Monk) on Nov 21, 2018 at 01:40 UTC

    I appreciate your effort but this is my confusion: The script works correctly at the command line and does not issue any warnings, despite use of the strict and warnings pragmas enabled. It is only when I try to run it using automated test tools do those warnings arise.

    Example: If I simply call reduce with 1 argument at the command line, it will be pushed onto the @answer array and returned.

    This tells me that the instantiation errors are in the way the test code interfaces with the code under test. I don't understand why the arguments aren't being passed to the appropriate subroutine.

    If I attempt to pass a list of scalar arguments in the test code, I get syntax errors when I run:

    perl -c test_file.t

    Also:

  • 1. What should I be doing in the return statements of this particular subroutine so they are easier to test?
  • 2. Should I be testing the subroutine individually, or indirectly accessing the sub through a call to main, as it appears to be done in the Perl Testing: A Developer's Notebook example?

      Oh! Silly me. You call:

      main('90 35 29 + 90 24 29')

      But main is:

      sub main { reduce(@ARGV); }

      so reduce doesn't see the parameters passed to main, it sees @ARGV which is empty. Change main to:

      sub main { reduce(@_); }

      Also:

      1. Whatever is natural for the sub. Because of its recursive implementation returning anything except a list from reduce doesn't make sense.
      2. There are two different test modes: Functional testing where you test the whole system works as a whole - call main, and unit testing where you test individual units work as advertised - call reduce.

      In this case "the whole" is a trivial wrapper around reduce so I'd go for testing reduce directly - a unit test.

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

        You last reply was very helpful! Running the test code manually, my debug print statements in the *.pl file show that the @answer array continues to contain the values from previous calls:

      • Test 1. @answer array returns 180 59 58 (test passes).
      • Test 2. @answer array returns 179 1 9 180 59 58 (test fails).
      • Test 3. @answer array returns 180 0 0 179 1 9 180 59 58 (test fails).
      • I tried adding
        my @answer = undef;
        to the test file main sub definition (thinking that this would get cleared on every call) but that did not work. What do you suggest now?