Pull to refresh

Тестирование рекурсии

Reading time2 min
Views6.3K

Есть пара весомых поводов не использовать рекурсию, но это не повод не использовать рекурсию вообще. Программы, во-первых, создаются программистами для программистов, и лишь во-вторых — программистами для компьютеров. В итоге, некоторыми годными программами могут пользоваться неподготовленные люди. Рекурсия имеет одно безусловное преимущество перед итерацией — читабельность. Когда программист создает программы для себе подобных, рекурсия имеет право на существование до тех пор, пока не докажет обратного (т.е. — не будет запущена на компьютере и не поперхнется реальными данными).


Тестирование — это, по сути, создание программ для программ, позволяющее программистам отодвигать порог непреодолимой сложности в разрабатываемых приложениях. Столкнувшись на днях с необходимостью написать юнит-тест для рекурсивного метода я был неприятно удивлен необходимостью мокировать сам тестируемый метод. Альтернатива — создавать такие входные данные, которые бы позволяли протестировать все ветки рекурсии в одном тестовом методе. В перспективе вырисовывалось не снижение сложности, а наоборот — ее увеличение. Порывшись в интернетах, я обнаружил кучу информации о том, чем нехороша рекурсия, массу советов, как перейти от рекурсии к итерации, но так и не нашел на русских формах того, что искал — как тестировать рекурсивный метод. Решив, что подготовить тестовые данные для трех проходов по коду — не такая уж непреодолимая сложность, отложил эту задачу до утра. Под катом решение, пришедшее в голову за ночь, позволяющее разбивать тестирование рекурсивных методов на части.


Рекурсивный метод


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

Рекурсивный метод с оберткой


Создаем обертку для метода и делаем так, чтобы сам метод вызывал только обертку, а обертка вызывала метод:


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

public function fooRecursive($arg1, $arg2)
{
    return $this->foo($arg1, $arg2);
}

Мокирование "обертки"


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');
}

Да, решение весьма незамысловатое, но, может кому-нибудь пригодится, и ему удастся потратить свою ночь на что-то более полезное.

Tags:
Hubs:
+2
Comments54

Articles

Change theme settings