Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

webmaster-lawnstarter's avatar

Laravel 5 PHPUnit Memory Allocation Exhausted

I have a large testing suite for our API built in Laravel. We just moved our code to the latest build of Laravel (from a pre 5.0 version) to get ready for the 5.1 release and the unit tests are now failing due to a memory allocation problem.

The memory allocation problem does not occur when running each tests class separately (not running the comprehensive test suite).

Has anyone ever dealt with something similar before? How did you all fix it.

vagrant@homestead:~/www/api.lawnstarter$ ./vendor/bin/phpunit
PHPUnit 4.8-dev by Sebastian Bergmann and contributors.

.................................................I.............  63 / 929 (  6%)
............................................................... 126 / 929 ( 13%)
............................................................... 189 / 929 ( 20%)
...........................FFF................................. 252 / 929 ( 27%)
.............PHP Fatal error:  Allowed memory size of 536870912 bytes exhausted (tried to allocate 53 bytes) in /home/vagrant/www/api.lawnstarter/vendor/routes.php on line 15
PHP Stack trace:
PHP   1. {main}() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/phpunit:36
PHP   3. PHPUnit_TextUI_Command->run() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/TextUI/Command.php:106
PHP   4. PHPUnit_TextUI_TestRunner->doRun() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/TextUI/Command.php:154
PHP   5. PHPUnit_Framework_TestSuite->run() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:438
PHP   6. PHPUnit_Framework_TestSuite->run() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/Framework/TestSuite.php:731
PHP   7. PHPUnit_Framework_TestCase->run() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/Framework/TestSuite.php:731
PHP   8. PHPUnit_Framework_TestResult->run() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/Framework/TestCase.php:699
PHP   9. PHPUnit_Framework_TestCase->runBare() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/Framework/TestResult.php:610
PHP  10. APIv1TestCase->setUp() /home/vagrant/www/api.lawnstarter/vendor/phpunit/phpunit/src/Framework/TestCase.php:739
0 likes
18 replies
jrewing's avatar

I have suddenly the same issue. It has worked flawlessly for months.

webmaster-lawnstarter's avatar

Just to give you an update on how we solved the problem (We didn't really but we got it to work).

From digging into this problem, it looks like phpunit does not de-allocate the built application from memory (database, cache, compiled resources, and the business logic) after running a test class. This was becoming even more problematic when running our project through CircleCI (they have even less memory than your vagrant box)

To solve it we built a test runner php script. It scans the test directory, and runs each test case in its down command line instance of phpunit. That way the memory is freed up after every test case is run. The runner keeps track of successes, failures and errors and compiles a report at the end.

For code coverage we use phpunit to generate ".cov" files for every test class that get compiled into one report by the test runner (all of this is built in command line functionality from phpunit).

TLDR; not really a solution but a way to continue working on your project. if anyone has fixed this before, all input would be much appreciated.

JeremyFrench's avatar

Just in case anyone else stumbles on this thread.

I had a high memory limit in my phpunit.xml file, but it was not applying. It looks like the config/local/app.php applies a 500Mb limit. Which will override any parameters you have for phpunit.

mondovo's avatar

@JeremyFrench I am having the same issue, I tried the teardown suggested by @willvincent but that didn't work, so was hoping could find the place you edited in config/app.php? I couldn't find any 500mb limit applied anywhere in my config, what is the line of code you added to override the memory limit.

mondovo's avatar

Ok my workaround was a couple of things:

I ran the test applying more memory: phpunit -d memory_limit=2048M

And there were some tests that I was using a var_dump or print_r, removing those automatically reduced the memory footprint

4 likes
jdavidbakr's avatar

Running into the same issue, I did the following to get enough memory (in bootstrap/autoload.php):

if(env('APP_ENV') == 'testing') {
    ini_set('memory_limit', '2G');
}

because to me having to type in the memory limit every time I launch phpunit is unacceptable.

I tried the tearDown idea to no avail (I have also had to add a DB::disconnect() call in tearDown to keep from having too many connections to the database). Ideally I'd like to solve why it's using so much memory instead of just raising the memory limit, already my tests are taking over 1.5GB and I'm just getting started. All of my testing is using the laravel crawler to verify everything is working on the site.

12 likes
wiseloren's avatar

If you're ok not have code isolation, you can use this to globalize your app and reuse it for all tests. Forwarning, this can cause all sorts of test interactions and means you have to remember to unmock things and do manual resets, but it does save a lot of memory and time...

/**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        global $app;

        if (is_null($app)) {
            $app = require __DIR__.'/../bootstrap/app.php';

            $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
        }

        return $app;
    }

To use this, you'll also want to override Laravel's default teardown with something like

/**
     * Closes down mockery
     */
    public function tearDown()
    {
        \Mockery::close();
        //parent::tearDown();
    }

Here's a sort of general overview of what this does from Laravel 4.2: https://github.com/laravel/framework/issues/1798

I'm wondering if you could save a bunch of RAM by having teardown unset $app or otherwise unload it using something like: http://kriswallsmith.net/post/18029585104/faster-phpunit

zer0pants's avatar

Another option is to run this test with the --process-isolation flag. It will slow down your tests but runs each test in a separate PHP process.

$phpunit tests --process-isolation

3 likes
Finesse's avatar

The simplest solution ever:

  1. Open your phpunit.xml file.
  2. Change the <phpunit> tag processIsolation attribute value to true. Example of a phpunit.xml file:
    <?xml version="1.0" encoding="UTF-8"?>
    <phpunit backupGlobals="false"
             backupStaticAttributes="false"
             bootstrap="vendor/autoload.php"
             colors="true"
             convertErrorsToExceptions="true"
             convertNoticesToExceptions="true"
             convertWarningsToExceptions="true"
             processIsolation="true"
             stopOnFailure="false">
    ...
    

How it works: it makes PhpUnit start a new PHP process for each test. This hack forces PHP to free all the memory after a test is over. It slows down the tests but it's the price for the low memory consumption and the reliability.

Or you can add a @runTestsInSeparateProcesses annotation to a test class doc block to make the test run in a separate process:

/**
 * @runTestsInSeparateProcesses
 */
class MyTest extends TestCase
{
    // ...
}
9 likes
mcblum's avatar

The process isolation trick seems to work, but does anyone know if there's some kind of big with phpunit + memory allocation? I'm monitoring our build server via our Grafana dashboard and I can see that it's using around 300mb of RAM at most, but it's crashing and saying "could not allocate more memory, 4gb used" or something. It's most certainly not allocating 4gb of ram to run these tests, but it thinks it is.

stratease@gmail.com's avatar

In our situation, some deployment / thirdparty systems don't give a lot of flexibility in how you can run phpunit. Although the phpunit.xml processIsolation="true" solution is probably the most elegant (should be the default?) it isn't always easily available.

The cleanup in the tearDown() worked for us.

Do NOT increase your php memory limit, as this just reinforces bad memory handling in your application. The typical web app should not require a higher memory limit (exceptions to this of course).

1 like
dimit's avatar

Using

    app()->forgetInstances();

in the teardown() method considerably reduced memory usage.

1 like
robjbrain's avatar

Not sure if it's just because it's an old post or not but the advice is not useful, forgetInstances() is already run within when you run a test case in Laravel, flush() is called which removes instances amongst other things.

// src/Illuminate/Foundation/Testing/TestCase.php
     /**
     * Clean up the testing environment before the next test.
     *
     * @return void
     */
    protected function tearDown(): void
    {
        if ($this->app) {
            $this->callBeforeApplicationDestroyedCallbacks();

            $this->app->flush();

            $this->app = null;
        }
    }
// src/Illuminate/Container/Container.php
    /**
     * Clear all of the instances from the container.
     *
     * @return void
     */
    public function forgetInstances()
    {
        $this->instances = [];
    }

    /**
     * Flush the container of all bindings and resolved instances.
     *
     * @return void
     */
    public function flush()
    {
        $this->aliases = [];
        $this->resolved = [];
        $this->bindings = [];
        $this->instances = [];
        $this->abstractAliases = [];
    }
2 likes
pjstew's avatar

I came across this issue today too. I agree with @robjbrain that the tearDown function is already clearing the instances with the flush method.

setting the processIsolation attribute to true on the phpunit element, as mentioned by @finesse worked well, although my tests now take about 3 times the time to run.

Not a huge issue, as we currently only have about 500 tests, but it would be nice to have it running super fast again.

Please or to participate in this conversation.