Time: 1.89 minutes, Memory: 526.00MB
OK (487 tests, 2324 assertions)
This is my phpunittest result from testing my Laravel API, the memory consumption just keeps increasing and feel like i tried all posts and answers across the internet for what keeps racking up memory when testing. From own debugging the App is trashed every test which it should.
Everything is pretty standard, with the createApplication method that looks like this.
public function createApplication()
{
// Ran out of memory
ini_set('memory_limit', '1024M');
$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
Came to the conclusion that the memory leak is in, which does not get cleaned up properly.
$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
Here is the simplest and the most reliable solution I've found. It doesn't have the disadvantages described in my previous answer.
Change the <phpunit>
tag processIsolation
attribute value to true
in the phpunit.xml
file. An example of the start of a proper 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. Running tests in separate processes 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 the doc block of a test class if you need to run in separate processes only certain tests:
/**
* @runTestsInSeparateProcesses
*/
class HeavyTest extends TestCase
{
// ...
}
I struggled for this for a long time temporarily solving the issue by turning on process isolation. That kind of worked but was super-slow and caused other issues.
The high memory usage is because the Laravel application object contains many references to other objects. PHP isn't very good at garbage collecting objects with many interconnected references so the objects hang around causing massive memory usage.
I solved this by extending builtin Application object and adding my own clean up method that's called after every test:
Note: This is for Laravel 4.2 but the same principal should work just as well with Laravel 5+.
Add app\foundation\Application.php
(this may differ depending on how you're using namespaces).
<?php
namespace App\Foundation;
class Application extends \Illuminate\Foundation\Application
{
/**
* Used during unit tests to clear out all the data structures
* that may contain references.
* Without this phpunit runs out of memory.
*/
public function clean()
{
unset($this->bootingCallbacks);
unset($this->bootedCallbacks);
unset($this->finishCallbacks);
unset($this->shutdownCallbacks);
unset($this->middlewares);
unset($this->serviceProviders);
unset($this->loadedProviders);
unset($this->deferredServices);
unset($this->bindings);
unset($this->instances);
unset($this->reboundCallbacks);
unset($this->resolved);
unset($this->aliases);
}
}
Ensure Laravel is autoloading the new class by editing the composer.json
file and adding app/foundation
to autoload
> classmap
. You may want to add the path to app/start/global.php
(ClassLoader::addDirectories) too.
Edit bootstrap\start.php
and change $app = new Illuminate\Foundation\Application;
to $app = new App\Foundation\Application;
.
Now edit your app/tests/TestCase.php
and add your own tearDown
method:
public function tearDown()
{
$this->app->clean();
unset($this->app);
unset($this->client);
parent::tearDown();
}
All this does is call the clean()
method after every test essentially removing the references before the object is garbage collected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With