24
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

Organization

PHPUnit の TestCase のメンバはテストが完了するまで解放されない

PHPUnit のドキュメントには、

http://phpunit.de/manual/3.7/ja/fixtures.html#fixtures.more-setup-than-teardown

setUp() と tearDown() は理屈上では対称的になるはずですが、実際にはそうではありません。
実際には、 tearDown() を実装する必要があるのは setUp() で外部リソース (ファイルやソケットなど) を割り当てた場合のみです。
もし setUp() で単に PHP オブジェクトを作成しただけの場合は、 一般には tearDown() は必要ありません。
しかし、もし setUp() で大量のオブジェクトを作成した場合には、 それらの後始末をするために tearDown() で変数を unset() したくなることもあるでしょう。
テストケースオブジェクト自体のガベージコレクションにはあまり意味がありません。

とありますが、次のようなテストを、

PHPUnitTestCaseMemberTest.php
<?php
class PHPUnitTestCaseMemberTest extends \PHPUnit_Framework_TestCase
{
    private $val;

    protected function setUp()
    {
        $this->val = str_repeat("x", 1024*1024);
    }

    /**
     * @dataProvider data
     */
    function test()
    {
        $this->assertTrue(true);
    }

    function data()
    {
        return array_fill(0, 1000, []);
    }
}

次のように実行するとエラーになります。

$ phpunit -d memory_limit=128M PhpUnitMemberTest.php
PHPUnit 3.7.27 by Sebastian Bergmann.

.............................................................   61 / 1000 (  6%)
......................................
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1048577 bytes) in /home/ng/work/sandbox/php/PhpUnitMemberTest.php on line 8

次のように修正するとエラーになりません。

PHPUnitTestCaseMemberTest.php
<?php
class PHPUnitTestCaseMemberTest extends \PHPUnit_Framework_TestCase
{
    private $val;

    protected function setUp()
    {
        $this->val = str_repeat("x", 1024*1024);
    }

    protected function tearDown()
    {
        $this->val = null;
    }

    /**
     * @dataProvider data
     */
    function test()
    {
        $this->assertTrue(true);
    }

    function data()
    {
        return array_fill(0, 1000, []);
    }
}
$ phpunit -d memory_limit=128M PhpUnitMemberTest.php
PHPUnit 3.7.27 by Sebastian Bergmann.

.............................................................   61 / 1000 (  6%)
.............................................................  122 / 1000 ( 12%)
.............................................................  183 / 1000 ( 18%)
.............................................................  244 / 1000 ( 24%)
.............................................................  305 / 1000 ( 30%)
.............................................................  366 / 1000 ( 36%)
.............................................................  427 / 1000 ( 42%)
.............................................................  488 / 1000 ( 48%)
.............................................................  549 / 1000 ( 54%)
.............................................................  610 / 1000 ( 61%)
.............................................................  671 / 1000 ( 67%)
.............................................................  732 / 1000 ( 73%)
.............................................................  793 / 1000 ( 79%)
.............................................................  854 / 1000 ( 85%)
.............................................................  915 / 1000 ( 91%)
.............................................................  976 / 1000 ( 97%)
........................

Time: 738 ms, Memory: 5.00Mb

OK (1000 tests, 1000 assertions)

TestCase クラスはテストの開始前にテストメソッドの数だけインスタンス化され(↑のように @dataProvider を使っている場合はさらにデータごと)、テストがすべて終わるまで解放されません(TestSuite が TestCase を握りっぱなしにするため)。

そのため、TestCase のメンバにオブジェクトや変数を保持させるとそれらはテストが完了するまで解放されません。


phpunit に --process-isolation オプションを付ければテスト毎に別プロセスになるため、最初の例(tearDown が無い版)でも大丈夫です。

$ phpunit -d memory_limit=128M --process-isolation PhpUnitMemberTest.php
PHPUnit 3.7.27 by Sebastian Bergmann.

.............................................................   61 / 1000 (  6%)
.............................................................  122 / 1000 ( 12%)
.............................................................  183 / 1000 ( 18%)
.............................................................  244 / 1000 ( 24%)
.............................................................  305 / 1000 ( 30%)
.............................................................  366 / 1000 ( 36%)
.............................................................  427 / 1000 ( 42%)
.............................................................  488 / 1000 ( 48%)
.............................................................  549 / 1000 ( 54%)
.............................................................  610 / 1000 ( 61%)
.............................................................  671 / 1000 ( 67%)
.............................................................  732 / 1000 ( 73%)
.............................................................  793 / 1000 ( 79%)
.............................................................  854 / 1000 ( 85%)
.............................................................  915 / 1000 ( 91%)
.............................................................  976 / 1000 ( 97%)
........................

Time: 32.51 seconds, Memory: 4.75Mb

OK (1000 tests, 1000 assertions)

その代わりテストにものすごく時間がかかるようになりますが。


TestCase に巨大なオブジェクトや変数を持たせることはあまりないと思いますが、Zend Framework のテストケース(zf1 の Zend_Test_PHPUnit_ControllerTestCase や zf2 の Zend\Test\PHPUnit\Controller\AbstractControllerTestCase )は、いろいろなオブジェクトをテストケースのメンバに持たせているので、注意が必要かもしれません。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
24
Help us understand the problem. What are the problem?