Home > Software design >  How do I write a unit test that successfully recognizes that a function threw an exception?
How do I write a unit test that successfully recognizes that a function threw an exception?

Time:12-05

I'm trying to write my very first unit tests ever in Laravel using PHPUnit. The tests I am writing are trying to verify that a fairly simple helper function works properly. The way it is written, my function expects one or two arguments and then tries to verify that the arguments are appropriate; if they are not, I throw InvalidArgumentExceptions. In other words, the function is working correctly if it throws an InvalidArgumentException on certain values being supplied for the arguments.

How do I write a unit test that says, in effect, if the function gets a decimal number in the second argument when it is expecting an int (or if an int is provided but it is out of range), an InvalidArgumentException should be thrown? What would the assertion look like? (I've looked at the list of available assertions but nothing looks like it would be appropriate for an exception.)

Here is my function, in its entirety:

namespace App\Helpers;

/**
 * This class contains a variety of helper functions useful to this app.
 */
class Helper2
{
    /**
     * This function chooses a random number of elements from an array and chooses
     * them at random.
     *
     * $array - the array of items from which elements will be selected
     * $numberOfElements - the number of elements from the array that the user desires.
     *
     * The array needs to be a simple array of strings, integers, etc.
     *
     * The $numberOfElements defaults to null. When the value is null, the function
     * will choose a random number of elements between 1 and the number of elements
     * in the array. If a non-null value greater than 0 is provided, that number of
     * elements will be returned by the function. If the number is larger than the
     * number of elements in the array, all elements of the array will be returned.
     */
    public static function choose_random_elements(array $array, int $numberOfElementsDesired = null)
    {
        // The first argument of the function must be an array. (No test necessary:
        // Laravel will not even let you code the function call with anything but
        // an array in the first argument.)
         /*    if (! is_array($array)) {
            throw new \InvalidArgumentException("The input array is not actually an array.");
        }
         */
        // Store the array in a collection.
        $myCollection = collect($array);

        // The second argument of the function must be numeric.
        if (! is_numeric($numberOfElementsDesired)) {
            throw new \InvalidArgumentException ("The number of elements desired must be a number.");
        }

        // The second argument of the function must be an integer.
        if (! is_integer($numberOfElementsDesired)) {
            throw new \InvalidArgumentException ("The number of elements desired must be an integer.");
        }

        // The second argument of the function cannot exceed the number of elements in the array.
        if ($numberOfElementsDesired > count($myCollection)) {
            throw new \InvalidArgumentException ("The number of elements desired cannot exceed the number of elements in the array.");
        }

        // The second argument of the function cannot be zero or less.
        if ($numberOfElementsDesired <= 0) {
            throw new \InvalidArgumentException("You cannot choose a negative number of elements from the array.");
        }

        // If no value was supplied for the second argument of the function, choose
        // an integer between 1 and the number of elements in the array.
        if (is_null($numberOfElementsDesired)) {
            $numberOfElementsDesired = rand(1, count($myCollection));
        }

        // If the number supplied for the second argument of the function exceeds
        // the number of elements in the array, set the second argument to the size
        // of the array.
        if ($numberOfElementsDesired > count($myCollection)) {
            $numberOfElementsDesired = count($myCollection);
        }

        // Choose a random number of elements at random from the collection and put them in a new collection.
        $randomSelectionsCollection = $myCollection->random($numberOfElementsDesired);

        // Convert the resulting collection into an array.
        $randomSelectionsArray = $randomSelectionsCollection->toArray();

        // Convert the array of selected elements into a string.
        $randomSelectionsString = implode(',', $randomSelectionsArray);

        echo "Function output: " . $randomSelectionsString . "\n";

        return $randomSelectionsString;
    }
}

The only alternative I can think of is writing the test in a try/catch block. I came up with this and it seems to work. Is it a reasonable way to write this kind of test or is there a better approach?

try {
    $result = $helper2->choose_random_elements(array(1, 6, "cat"), -4);
} catch(InvalidArgumentException $excp) {
    $this->assertEquals("You cannot choose a negative number of elements from the array.", $excp->getMessage());
}

I am running Laravel 9.

CodePudding user response:

It is very simple indeed, your test should looke like the following:

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("You cannot choose a negative number of elements from the array.");

Helper2::choose_random_elements(array(1, 6, "cat"), -4);
  • Related