catching warnings, notices and deprecations with phpunit 9.5 - phpunit

Quoting https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#testing-php-errors-warnings-and-notices , "By default, PHPUnit converts PHP errors, warnings, and notices that are triggered during the execution of a test to an exception". Bearing that in mind, here's my unit test:
<?php
class DemoTest extends PHPUnit\Framework\TestCase
{
public function testDemo()
{
try {
trigger_error('zzz', E_USER_DEPRECATED);
} catch (\Throwable $e) {}
}
}
When I run vendor/bin/phpunit on that with PHPUnit 9.5.10 (on either PHP 8.0.9 or PHP 8.1.0) I get the following:
PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
R 1 / 1 (100%)
Deprecated: zzz in C:\path\to\code\tests\DemoTest.php on line 8
Time: 00:00.007, Memory: 6.00 MB
There was 1 risky test:
1) DemoTest::testDemo
This test did not perform any assertions
C:\path\to\code\tests\DemoTest.php:5
OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Risky: 1.
I don't want to see the error in the output and, if an exception isn't being thrown, then $this->expectException('PHPUnit\\Framework\\Error\\Deprecated') isn't gonna work for me.
Any ideas?

In the changelog of 9.5.10 it is stated that:
PHPUnit no longer converts PHP deprecations to exceptions by default (configure convertDeprecationsToExceptions="true" to enable this)
So this is expected. Technically one could even argue that the statement about "errors, warnings, and notices" is correct, as deprecations are their own category.
You typically configure this in a XML configuration file, provided with -c.
Also mind the note on the documentation page about the error_reporting ini/runtime setting: It must not exclude deprecations aswell (E_ALL is fine).

In case someone else arrives here and the convertDeprecationsToExceptions = "true" setting won't work then also check if you don't have # in front of the trigger_error() statement like for example the Drupal's deprecation policy suggests.
That is on purpose excluded from PHPUNIT v9.5 error handler:
/*
* Do not raise an exception when the error suppression operator (#) was used.
*
* #see https://github.com/sebastianbergmann/phpunit/issues/3739
*/
if (!($errorNumber & error_reporting())) {
return false;
}

it seems that intention was to throw and catch an exception, but using trigger_error is not the same (it is not throw an exception). There is a great answer explaining difference.
If your intention is to handle deprecation errors, then I suggest to use set_error_handler where there is a possibility to custom handle E_DEPRECATED messages.

Related

How to make symfony display exceptions in functional testing with phpunit

When I run unit tests with PhpUnit in console all exceptions are display immediately there. However when I moved to functional testing I cannot see exceptions which occur.
First I tried symfony WebTestCase as it was said here. So I called $client with needed parameters and I have a response. But it took time to me to understand that this behaviour is just like I manually open a page I want to test. And a response from $client->request contains text (html). Yes errors are show there but there are pretty much html and it is really hard to find the exception. Yes, I can use $crawler->filter('.text-exception')->first()->text() but I want so that exception was visible like in unit test. When I tested commands exceptions are shown by PhpUnit as well as normal.
I tried to copy code from web/app_dev.php to test case. But it is the same. I have only html response.
So how can I make it so that PhpUnit display exceptions in functional testing like in unit testing?
You can use profiler to do it:
$client->enableProfiler();
$client->request('GET', '/');
$profile = $client->getProfile();
$exceptionProfile = $profile->getCollector('exception');
if ($exceptionProfile->hasException()) {
$message = sprintf(
"No exception was expected but got '%s' with message '%s'. Trace:\n%s",
get_class($exceptionProfile->getException()),
$exceptionProfile->getMessage(),
$exceptionProfile->getException()->getTraceAsString()
);
throw new AssertionFailedError($message);
}

Twig's dump function returns a blank screen

I am using Twig's dump function in Symfony2. I have configured Symfony according to its instructions.
I have a page variable, and an orders array. dump works on page, but not orders. When I call it on orders, I get a white screen - no php errors or anything. I have no idea how to debug this.
Any ideas?
A little explanation
A white (blank) screen in this case means the PHP fatal error: Allowed memory size exhausted. During my investigation, I found that twig uses thevar_dump function while I have VarDumper component installed.
I think its made to work along in case the VarDumper component is not installed, but twig's dump() function covered in symfony's VarDumper component documentation like a complex solution, that's strange.
So, using VarDumper's dump() function instead of native var_dump() solves the memory problem (because VarDumper limits result dump collection to adequate amount). Also VarDumper's dump() give more convenient results - you can click on tree leafs to show/hide its content.
What exactly do you need to do
Install VarDumper component if not installed
Go to file: vendor/twig/twig/lib/Twig/Extension/Debug.php
Find twig_var_dump function
Replace all var_dump() calls to dump()
Delete/comment ob_start() + ob_get_clean() construction (which is needed
if you use var_dump() as it echoes data immideately, but dump() acting
more intelligent)
OR
copy + replace the entire function using this:
function twig_var_dump(Twig_Environment $env, $context)
{
if (!$env->isDebug()) {
return;
}
$count = func_num_args();
if (2 === $count) {
$vars = array();
foreach ($context as $key => $value) {
if (!$value instanceof Twig_Template) {
$vars[$key] = $value;
}
}
dump($vars);
} else {
for ($i = 2; $i < $count; $i++) {
dump(func_get_arg($i));
}
}
}
PS: Question's asked in 2013, but I hope it helps because I had this problem now.
My context:
"symfony/symfony": "2.5.*"
"symfony/var-dumper": "~2.6"
I had same issue, increasing the memory_limit up to 1Gb didn't help.
In my case I want to call the
{{ dump() }}
with no parameters (to see all the available vars in the context of the current template. In my case the following helped: https://stackoverflow.com/a/11500141/2166261 , though the memory_limit issue still persists)
Most likely you are hitting your PHP memory limit. Try increasing it in the php.ini
There is probably no single answer to this problem, as a range of things could cause it. As other people have suggested, increasing the memory limit for PHP may help. In my case this did not help, however, I was able to resolve the issue by installing xdebug.
So far, it seems then that a checklist for resolving this issue would look something like this:
Ensure that the latest version of xdebug is installed
If that doesn't resolve it, try increasing PHP memory limit
The Arkemlar answer works fine.
BUT
after that the twig dump() caused a RuntimeException in the functional test with phpunit in my case.

Disabling unit-test compilation in Boost.Build

Boost.Build documentation is quite laconic when it comes to testing.
All tests in my project are defined using unit-test rule.
The only property mentioned, by the documentation, is testing.launcher, but that can only disable tests' execution when set to testing.launcher=true.
How to completely disable compilation of unit-test rules? I would like to do that temporarily, for example, by setting a property from commandline. I could not find any information how to do that or any reference documentation for other testing.* properties.
If you mean disabling them by default? You can do it by adding "explicit ;" for each unit test. If you have many such targets you can save some typing and declare a rule that does it for you, plus declaring the unit test like:
rule explicit-unit-test ( target : source : properties * )
{
unit-test $(target) : $(source) : $(properties) ;
explicit $(target) ;
}
If you want something else.. I guess you need to better explain your question because I can't think of what else you could want.
As I read most of the Boost.Build documentation and the relevant part of its code I found no way to temporary disable building specific rule or the set of targets (for example by matching tests' targets with a regular expression).
It is, also, worth noting, that unit-test was deprecated in favor of the new testing rules: run, run-fail, compile, compile-fail, link, link-fail.
Now, probably, I'm going to create my own rule, as in #GrafikRobot's answer, but instead of making the target explicit I will make the rule empty in the presence of a certain feature.
I use explicit test suites for this purpose as in
explicit X ;
test-suite X
:
[ run test1.cpp ]
[ run test2.cpp ]
[ run test3.cpp ]
[ run test4.cpp ]
;
You will need to request explicitly the execution of the tests in the test-suite X using
bjam X

"Undefined property: stdClass"

This error suddenly occurred when Pressflow was added to our Drupal installation. It is coming from a custom module that, prior to Pressflow seemed to work fine. After the addition of Pressflow, running Drush causes this error to display on screen.
The function that is identified as the source of the error looks like this:
function user_search_location_get(&$user) {
if (count($user->user_location_pref)) { // This line is causing the error.
return $user->user_location_pref;
}
// …
}
The error message is the following:
WD php: Notice: Undefined property: stdClass::$user_location_pref in user_search_location_get()
Short answer, in your custom module, you should check if that property exists before you count it. That, or make sure the $user object has that property before you use it.
if (isset($user->user_location_pref) && count($user->user_locaion_pref) > 0) {
return $user->user_locaion_pref;
}
While it is a little more work, when you start developing with notices turned on, you will find errors in your code that otherwise would have not appeared till later, and would have been more difficult to track down.
In your previous environment or install, the PHP error reporting was probably set to not show notices. While I recommend keeping notices on and making your code work with them, you can turn them off through the Drupal 7 UI. Configuration -> Development -> Logging and Errors. Then set the value to 'Errors and Warnings' ... Otherwise, you can set your error reporting level in your php.ini to report all except notices.
Note that Drupal 6 did not force notice reporting on, while Drupal 7 does. That prompts this type of question a lot.
If this is your only notice issue though, it makes more sense to just correct your custom module.

Conditional compilation "else"

In AS3 you can pass a constant to the compiler
-define+=CONFIG::DEBUG,true
And use it for conditional compilation like so:
CONFIG::DEBUG {
trace("This only gets compiled when debug is true.");
}
I'm looking for something like #ifndef so I can negate the value of debug and use it to conditionally add release code. The only solution I've found so far was in the conditional compilation documentation at adobe and since my debug and release configurations are mutually exclusive I don't like the idea of having both DEBUG and RELEASE constants.
Also, this format works, but I'm assuming that it's running the check at runtime which is not what I want:
if (CONFIG::DEBUG) {
//debug stuff
}
else {
//release stuff
}
I also considered doing something like this but it's still not the elegant solution I was hoping for:
-define+=CONFIG::DEBUG,true -define+=CONFIG::RELEASE,!CONFIG::DEBUG
Thanks in advance :)
This works fine and will strip out code that won't run:
if (CONFIG::DEBUG) {
//debug stuff
}
else {
//release stuff
}
BUT this will be evaluated at runtime:
if (!CONFIG::DEBUG) {
//release stuff
}
else {
//debug stuff
}
mxmlc apparently can only evaluate a literal Boolean, and not any kind of expression, including a simple not.
Use the if / else construct : the dead code will be removed by the compiler and it will not be tested at runtime. You will have only one version of your code in your swf.
If you are not sure use a decompiler or a dump tool to see what really happens.
http://apparat.googlecode.com/files/dump.zip
http://www.swftools.org/
...
While Patrick's answer fulfills the question's criteria, it does not cover all use cases. If you are in an area of code that allows you to use an if/else statement then this is a good answer. But if you are in a place where you cannot then you will need a better solution. For example, you may want to do something like this to declare a constant in a class:
private var server:String = "http://localhost/mystagingenvironment";
or for a live release:
private var server:String = "http://productionserver.com";
(this is an example and I'm not advocating this as production code).
I use xml configs and use the loadConfig+="myconfig.xml" to do my configuration instead of passing large numbers of command line params. So in the <compiler> section of your xml config:
<define>
<name>CONFIG::debug</name>
<value>false</value>
</define>
<define>
<name>CONFIG::release</name>
<value>!CONFIG::debug</value>
</define>
This works well for all use cases:
CONFIG::debug
{
private var server:String = "http://localhost/mystagingenvironment";
}
CONFIG::release
{
private var server:String = "http://productionserver.com";
}
This has the additional benefit of working consistently across applications. It also does not rely on the 'optimize' flag being true, like Patrick's answer (although I think we can assume that 99.999999% of all swfs have optimize=true, I only set it to false when the optimizer breaks my AS3).
It does have the drawback that it doesn't compile all code paths, just the ones that are included. So if you're not using a build server to create release builds and tell you when things break, be prepared for surprise errors when you do your release build ("But it compiled in debug! Crap, I need this to launch now!").
Just my two cents about Chris Hill's answer (which is the solution I also use regularly): it seems that using the loadConfig+="myconfig.xml" option makes the compiler searching for the myconfig.xml file in the Flex SDK directory whereas the -load-config+=myconfig.xml option makes it searching for the myconfig.xml file in the project's directory, which is the behavior I strongly prefer as you can then easily distribute this file with your project sources...

Resources