I am trying to generate code test coverage for my PHP project with PHPUnit and phpdbg using the following command:
phpdbg -dmemory_limit=512M -qrr ./bin/phpunit -c .phpunit.cover.xml
This works perfectly fine:
PHPUnit 6.2.4 by Sebastian Bergmann and contributors.
........ 8 / 8 (100%)
Time: 114 ms, Memory: 14.00MB
OK (8 tests, 13 assertions)
Generating code coverage report in HTML format ... done
However, when I use the exact same command in a docker container:
docker run -it --name YM4UPltmiPMjObaVULwsIPIkPL2bGL0T -e USER=sasan -v "/home/sasan/Project/phpredmin:/phpredmin" -w "/phpredmin" --user "1000:www-data" php:7.0-apache phpdbg -dmemory_limit=512M -qrr ./bin/phpunit -c .phpunit.cover.xml
I get the following error:
PHPUnit 6.2.4 by Sebastian Bergmann and contributors.
[PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 561514763337856 bytes) in /phpredmin/vendor/phpunit/phpunit/src/Util/GlobalState.php on line 166]
I don't understand why PHPUnit needs to allocate 561514763337856 bytes of memory. I suspect it gets stuck in a loop, but why this does not happen outside of the container? Here is my PHP version on my machine:
PHP 7.0.22-0ubuntu0.17.04.1 (cli) (built: Aug 8 2017 22:03:30) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.22-0ubuntu0.17.04.1, Copyright (c) 1999-2017, by Zend Technologies
And here is the .phpunit.cover.xml file:
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
cacheTokens="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="true"
stopOnFailure="true"
stopOnIncomplete="false"
stopOnSkipped="false"
stopOnRisky="false"
timeoutForSmallTests="1"
timeoutForMediumTests="10"
timeoutForLargeTests="60"
verbose="false">
<testsuites>
<testsuite name="PhpRedmin PHP source">
<directory>src-test/</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-html" target="cover/" lowUpperBound="35"
highLowerBound="70"/>
</logging>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src-test/</directory>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>
-- Edit1 --
I found that it has something to do with #runInSeparateProcess. When I remove the test that has #runInSeparateProcess then it starts to work. But still I don't know what is the problem
-- Edit2 --
Also I found out that If I don't mount my code directory in the Docker container everything works fine
Add '-d memory_limit=-1' before your test
if use composer use the below code
./vendor/bin/simple-phpunit -d memory_limit=-1 tests/
if you use phpdbg use the below code
phpdbg -d memory_limit=-1 -qrr vendor/bin/phpunit --coverage-text
When we use #runInSeparateProcess, PHPUnit will attempt to serialize included files, ini settings, global variables, and constants to pass them to the new process. In this case, it looks like PHPUnit encountered a recursive scenario when serializing one of these items, which exhausted the memory available to the PHP process. We need to determine what changed between your local environment and the Docker container.
First, we can try disabling this serialization behavior to verify that we should proceed along this path. Add the following #preserveGlobalState annotation to the test method that fails:
/**
* #runInSeparateProcess
* #preserveGlobalState disabled
*/
public function testInSeparateProcess()
{
// ...
}
If this resolves the problem, or if we get a new error, we can begin looking for differences in the Docker container that may cause the issue. Without more visibility into the code and environment, it's hard to suggest where to start, but here are some ideas:
Compare the output of php -i from each environment. Look out for PHP extensions that exist in one, but not the other.
Use phpdbg to set a breakpoint and step through the code. We're already using it to generate coverage, but it's also a useful debugging tool. We're looking for the item that causes the infinite recursion. Note that we'll need to set the breakpoint before PHPUnit executes the test case such as in the bootstrap file or PHPUnit source code (line 810 of TestCase may work).
When bind-mounting a volume, make sure the www-data user in the container has the same UID as the user that owns the files on the host.
Try running the test without the memory limitation. Obviously we can't allocate as much as the error suggests we may need, but the memory limit may mask another underlying problem. We can kill the container if needed.
I couldn't reproduce this issue using a dummy test in a similar environment (as close as I could get—same container with volume), so the code under test may contribute to the problem.
Your mounted folder probably has all vendors and cache files. Try mounting only the source folder.
This is phpdbg bug. Note how much memory it tries to allocate? It is insane. I tried hard to figure out where the bug exactly: if test script full name (directory + script file name) exceeds certain size then it overflows the stack (yeah, stackoverflow ;) and overwrites integer which specifies how much memory must be allocated. Then normal error handling kicks in: not enough memory to allocate such insane amount. So this is for Krakjoe to fix. Or may be I will make a patch for it in my spare time. If you need to have resolution right now: make sure that directory path your script is in is short.
Related
using version 8.1.6, and Given the following phpunit.xml :
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="SlimSkeleton">
<directory>tests</directory>
<exclude>./tests/Functional/BaseTestCase.php</exclude>
</testsuite>
</testsuites>
</phpunit>
And the following directory structure :
./tests/Functional/serviceA/...
./tests/Functional/BaseTestCase.php
I keep getting the following output :
...
1) Warning
No tests found in class "Tests\Functional\BaseTestCase".
...
I run the suitr via a scripts command in composer.json :
{
...
"scripts": {
"test": "phpunit"
}
}
Is it expected ? Is there a way to silence this warning ?
phpunit by default finds *Test.php, so even without <exclude> in phpunit.xml it would ignore BaseTestCase.php by running composer test.
Using tests instead of tests/EmailTest would instruct the PHPUnit command-line test runner to execute all tests found declared in *Test.php sourcecode files in the tests directory.
https://phpunit.de/getting-started/phpunit-8.html
I see "No tests found" warning if I specify command-line argument like the below. But this is not the intended usage of BaseTestCase.php from Slim-Skelton.
$ composer test tests/Functional/BaseTestCase.php
> phpunit 'tests/Functional/BaseTestCase.php'
PHPUnit 8.1.6 by Sebastian Bergmann and contributors.
W 1 / 1 (100%)
Time: 21 ms, Memory: 4.00 MB
There was 1 warning:
1) Warning
No tests found in class "Tests\Functional\BaseTestCase".
WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.
I have a lot of problems trying to intall phpunit, maybe my knowledge isn't enought or the guide is very incomplete.
First, the install, I tried all the ways, globally, with "downloaded PHAR file directly" or with "sudo apt-get install phpunit" but when I tried to do:
$phpunit -v
bash: /usr/bin/phpunit: No chuch file or directory
if I do:
$ ll /usr/local/bin (I know, the path is different, other unexplicable event)
-rwxr-xr-x 1 user user 2784899 abr 29 17:09 phpunit*
but
$ sudo phpunit --version
PHPUnit 7.1.5 by Sebastian Bergmann adn contributors.
ok, looks better, so I tried to make the first example
<?php
use PHPUnit\Framework\TestCase;
class StackTest extends TestCase
{
public function testPushAndPop()
{
$stack = [];
$this->assertSame(0, count($stack));
array_push($stack, 'foo');
$this->assertSame('foo', $stack[count($stack)-1]);
$this->assertSame(1, count($stack));
$this->assertSame('foo', array_pop($stack));
$this->assertSame(0, count($stack));
}
}
but it give me the next error:
PHP Fatal error: Class 'PHPUnit\Framework\Testcase' not found in /var/www/html/phpunit/index.php on line 4
I'm using Ubuntu 18 and php 7.2
Any idea?
When you are running PHPUnit from the command line, you also need to include a 'bootstrap' file - it can be as simple as the composer autoload.php file:
phpunit --bootstrap vendor/autoload.php
Longer term, that configuration would be put into the phpunit.xml file so it is read, and run automatically by PHPunit.
<!-- file: phpunit.xml
src/autoload.php would also include the ./vendor/autoload.php file
and do any other locally required setup -->
<phpunit bootstrap="src/autoload.php">
<testsuites>
<testsuite name="money">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
Ok, I start to understand some things.
First #Sebastian Bergmann give me the clue, with this example, it works.
But if you start with the documentation you never found it. I think it's an error and can confuse a begginer like me.
Yet I can't install phpunit with PHAR or globally, maybe it could be a future new post.
Thanks for all
I have a fairly simple problem that I cannot seem to figure out.
I am developing an OOP-based PHP application using the Composer Dependency Manager, PHPUnit for testing. I am hosting the repository on GitLab and am using GitLab-CI to run the PHPUnit tests.
My file directory is fairly simple:
├──_data
├──_2016
├──_federal
├──fit.json
├──_libs
├──_paycheckCalculator
├──paycheck.php
├──taxCalc.php
├──_public_html
├──index.php
├──_vendor
├──[composer dependencies]
├──autoload.php
├──_tests
├──paycheckTest.php
├──taxCalcTest.php
├──_templates
├──[Twig templates]
taxCalc.php contains:
public static function calcFIT($taxableWages, array $taxStatus, int $frequency = 52):float {
$fitFile = "../data/2016/federal/fit.json";
...
That works just fine on my production server and I can run the PHPunit tests just fine via PHPUnit integration with PhpStorm, but when I try to get GitLab-CI to work I consistently get an error:
...
$ vendor/bin/phpunit --configuration phpunit.xml PHPUnit 5.5.4 by
Sebastian Bergmann and contributors.
EE..EII 7
/ 7 (100%)
Time: 32 ms, Memory: 4.00MB
There were 3 errors:
1) paycheckTest::calcNetTest
file_get_contents(../data/2016/federal/fit.json): failed to open
stream: No such file or directory
/builds/calebrw/paycheckCalculator/libs/paycheckCalculator/taxCalc.php:100
/builds/calebrw/paycheckCalculator/libs/paycheckCalculator/paycheck.php:49
/builds/calebrw/paycheckCalculator/libs/paycheckCalculator/paycheck.php:28
/builds/calebrw/paycheckCalculator/tests/paycheckTest.php:34
2) paycheckTest::calcTaxesTest
file_get_contents(../data/2016/federal/fit.json): failed to open
stream: No such file or directory
/builds/calebrw/paycheckCalculator/libs/paycheckCalculator/taxCalc.php:100
/builds/calebrw/paycheckCalculator/tests/paycheckTest.php:58
3) taxCalcTest::calcFITTest
file_get_contents(../data/2016/federal/fit.json): failed to open
stream: No such file or directory
/builds/calebrw/paycheckCalculator/libs/paycheckCalculator/taxCalc.php:100
/builds/calebrw/paycheckCalculator/tests/taxCalcTest.php:53
ERRORS! Tests: 7, Assertions: 11, Errors: 3, Incomplete: 2. ERROR:
Build failed: exit code 1
My .gitlab_ci.yml is as follows:
# Select image from https://hub.docker.com/_/php/
image: php:7.0
# Select what we should cache
cache:
paths:
- vendor/
before_script:
# Install git, the php image doesn't have installed
- apt-get update -yqq
- apt-get install git -yqq
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install all project dependencies
- php composer.phar install
PHPUnit:testsuite:
script:
- vendor/bin/phpunit --configuration phpunit.xml
My phpunit.xml file contains:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false">
<testsuites>
<testsuite name="Paycheck Tests">
<directory>tests/</directory>
</testsuite>
</testsuites>
<php>
<includePath>/builds/calebrw/paycheckCalculator</includePath>
</php>
</phpunit>
Please note that I've used this with or without the <includePath> tag and there is no difference if I use <includePath>/../</includePath> or anything else for that matter.
I appreciate any help you can give.
EDIT:
I finally got this to work. I added a function (in the global space for now) to my config.php file:
/**
* Required Functions
*/
function getDirectory(bool $html = false):string
{
$htmlBaseDirectory = '/webprojects/paycheckCalculator';
if ($html == true) {
return $htmlBaseDirectory;
} else {
return dirname(__DIR__);
}
}
That meant I could update my index.php:
require_once('config.php'); // Configuration Variables
require_once( getDirectory() . '/vendor/autoload.php'); // Composer Autoload
$dir = getDirectory(true) . $htmlPublicDirectory; // Combined variable needed for Twig compatibility
but I was still having problems with the GitLab-Ci runner having yet a completely different environment that doesn't call my config.php at all, so I added a fix (or hack really) to get the test to pass to taxCalc.php:
if (getenv('CI_BUILD_ID') !== false) {
define('MAIN_PATH', '/builds/calebrw/paycheckCalculator/');
} else {
define('MAIN_PATH', dirname(__DIR__));
}
...
class taxCalc
{
...
public static function calcFIT($taxableWages, array $taxStatus, int $frequency = 52):float {
$fitFile = MAIN_PATH . "/data/2016/federal/fit.json";
And now the build passes.
Thanks for all the help to both people who responded.
Make sure that the file is commited (usually you don't commit data you may place it under resources). I think you did already.
The second thing is also just a suggestion:
define a PATH constant and use this. Because in your case you never know where is your current working directory.
Define a bootstrap file in phpunit and define the MAIN_PATH. Example:
<?php
define('MAIN_PATH', dirname(__DIR__));
require MAIN_PATH . '/vendor/autoload.php';
In the index you have to provide this MAIN_PATH too and in calcFit you write:
<?php
function calcFit() {
$fitFile = MAIN_PATH . '/data/2016/federal/fit.json';
}
The issue is very probably that you are using a relative path in your require. See here for explanations and solutions : PHP - Failed to open stream : No such file or directory
I have already posted this question on php unit first test symfony
I installed phpunit via the composer as a per project installation.
When trying vendor/bin>phpunit -c ../../app every thing is ok and I get a positive answer.
Whereas this command give the answer to all the tests in the tests directory.
But I want the result to every test alone.
When trying /vendor/bin>phpunit -c ../../src/xxx/Bundle/tests/entity/yyy.php and I get the following message : could not load c:\wamp\www\symfony\src/xxx/Bundle/tests/entity/yyy.php Parse PI : PI php never end ... Start ttag expected, '<' not found
and when trying /vendor/bin>phpunit -c ../../src/xxx/Bundle/tests/entity/yyy and I get the following message : could not read "..\..\src/xxx/Bundle/tests/entity/yyy"
Could anybody help me to know how should I write the command and from where execute it???
Any ideas???
Don't use the -c option here. The -c option is a shortcut for --configuration and it points to the directory of a PHPunit configuration file (like app/phpunit.xml.dist). That configuration tells PHPunit where to look for the test classes and some other configuration, like the bootstrap file.
If you want to run tests for a specific test, you can do it like phpunit path/to/tests/MyTest.php. But you'll loose the autoloading then. To get that back, you can use the --bootstrap option to point to the bootstrap file. So it'll be phpunit --bootstrap vendor/autoload.php path/to/tests/MyTest.php.
If you want to run this command more often, you can better edit the app/phpunit.xml.dist file and create a new suite:
<?xml version="1.0" encoding="utf-8" ?>
<phpunit ...>
<!-- ... -->
<testsuites>
<testsuite name="MyBundle">
<file>path/to/tests/MyTest.php</file>
</testsuite>
</testsuites>
<!-- ... -->
</phpunit>
And then run: phpunit -c app --testsuite MyBundle
Happened with me too, but in fact we are misreading the documentation you are forgetting 'app' in the command line look:
phpunit -c app src/AppBundle/Tests/Util/CalculatorTest.php
Note the app parameter in command sentence.
I've just installed phpunit via pear in a mac osx 10.7 and everything works fine except I got memory limit errors (xdebug enabled for reports).
I tried to add the -d memory_limit=512M parameter to phpunit but it is not applying because, on the very first error, I added var_dump(ini_get('memory_limit')); exit; and it prints string(3) "32M"
So, why is it not being applied?
Besides that, if I run
php -d memory_limit=256M -r "echo ini_get('memory_limit');"
it echoes "256M"
Is it possible that phpunit is not executing same php?
For those who reach this thread and need to get the configuration for phpunit.xml configuration file:
<php>
<ini name="memory_limit" value="512M" />
</php>
More on section "Setting PHP INI settings, Constants and Global Variables" at
https://phpunit.de/manual/6.5/en/appendixes.configuration.html
Yes you can set every php option with phpunit -d that can be set with ini_set.
You already opened a bug over in the phpunit bug tracker but well I'm going for the more verbose answer here
Reproduce to show it works in general:
echo "<?php var_dump(ini_get('memory_limit')); " > foo.php
phpunit -d memory_limit=12M --bootstrap foo.php
Produces:
string(3) "12M"
PHPUnit 3.6.5 by Sebastian Bergmann.
But phpunit only applies this option once before the first test is run!
So chances are your code is somewhere changing the memory limit back to 32M which is something phpunit can't "fix".
Same goes for setting the memory limit in the phpunit.xml file.
If you don't mind about the speed of the tests, found this thread very useful as it's better handling of memory for your app.
Basically; In your phpunit.xml, under the <phpunit> tag, set the processIsolation to true i.e
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
...
processIsolation="true"
... >
Alternatively,
You can just disable memory limiting altogether under the <php> tag, set the memory_limit to -1 i.e
<php>
...
<ini name="memory_limit" value="-1"/>
...