Back to Home

Recursion testing

php · phpunit · recursion

Recursion testing

    There are a couple of good reasons not to use recursion, but this is not a reason not to use recursion at all. Programs, firstly, are created by programmers for programmers, and only secondly, by programmers for computers. As a result, some suitable programs can be used by untrained people. Recursion has one definite advantage over iteration - readability. When a programmer creates programs for his own kind, recursion has the right to exist until it proves the opposite (that is, it will not be launched on the computer and choked with real data).


    Testing is, in fact, the creation of programs for programs that allows programmers to push the threshold of insurmountable complexity in the developed applications. Faced the other day with the need to write a unit test for a recursive method, I was unpleasantly surprised by the need to wet the tested method itself. An alternative is to create such input data that would allow testing all recursion branches in one test method. In the future, it was not a decrease in complexity looming, but rather an increase in complexity. Having rummaged through the Internet, I found a lot of information about what recursion is not good, a lot of tips on how to switch from recursion to iteration, but I never found what I was looking for in Russian forms - how to test the recursive method. Having decided that preparing test data for three code passes is not such an insurmountable difficulty, postponed this task until the morning. Under the cut, the solution came to mind over night, allowing to break the testing of recursive methods into parts.


    Recursive method


    public function foo($arg1, $arg2)
    {
        //...
        $out = $this->foo($in1, $in2);
        //...
    }

    Fractal wrapper method


    We create a wrapper for the method and make the method itself call only the wrapper, and the wrapper calls the method:


    public function foo($arg1, $arg2)
    {
        //...
        $out = $this->fooRecursive($in1, $in2);
        //...
    }
    public function fooRecursive($arg1, $arg2)
    {
        return $this->foo($arg1, $arg2);
    }

    Wrapping Mocking


    public function test_foo()
    {
        /* create mock for wrapper 'fooRecursive'*/
        $obj = \Mockery::mock(\FooClass::class . '[fooRecursive]');
        $obj->shouldReceive('fooRecursive')->once()
            ->andReturn('out');
        /* call recursive method 'foo' */
        $res = $obj->foo('arg1', 'arg2');
    }

    Yes, the decision is very straightforward, but maybe someone will come in handy, and he will be able to spend his night on something more useful.

    Read Next