Currently, when writing a unit test for my Symfony2 bundle, I explicitly set ini_set('display_errors', 1) to make sure I see any errors I made in writing my unit test. This is particularly helpful for fatal errors, such as those resulting from typos in method names.
Example:
# src/Foo/BarBundle/Tests/Security/RoutePermissionCheckerTest.php
namespace Foo\BarBundle\Tests\Security;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
class RoutePermissionCheckerTest extends TestCase
{
public function testIndex()
{
ini_set('display_errors', 1);
$this->nonExistentMethod();
}
}
Output:
Configuration read from /Users/maurits_dekkers/Sites/hobby/symfony/saas-seed/app/phpunit.xml.dist
...
Fatal error: Call to undefined method Bb\UserBundle\Tests\Security\RoutePermissionCheckerTest::nonExistentMethod() in /Users/maurits_dekkers/Sites/hobby/symfony/saas-seed/src/Bb/UserBundle/Tests/Security/RoutePermissionCheckerTest.php on line 18
Call Stack:
0.0314 635432 1. {main}() /Users/maurits_dekkers/pear/bin/phpunit:0
0.5501 1191328 2. PHPUnit_TextUI_Command::main() /Users/maurits_dekkers/pear/bin/phpunit:46
0.5502 1192056 3. PHPUnit_TextUI_Command->run() /Users/maurits_dekkers/pear/share/pear/PHPUnit/TextUI/Command.php:129
1.2241 7133440 4. PHPUnit_TextUI_TestRunner->doRun() /Users/maurits_dekkers/pear/share/pear/PHPUnit/TextUI/Command.php:176
1.2619 7653720 5. PHPUnit_Framework_TestSuite->run() /Users/maurits_dekkers/pear/share/pear/PHPUnit/TextUI/TestRunner.php:349
14.0128 37804208 6. PHPUnit_Framework_TestSuite->run() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestSuite.php:705
14.0130 37804608 7. PHPUnit_Framework_TestSuite->runTest() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestSuite.php:745
14.0130 37804608 8. PHPUnit_Framework_TestCase->run() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestSuite.php:775
14.0131 37804608 9. PHPUnit_Framework_TestResult->run() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestCase.php:783
14.0131 37805600 10. PHPUnit_Framework_TestCase->runBare() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestResult.php:648
14.0134 37846840 11. PHPUnit_Framework_TestCase->runTest() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestCase.php:838
14.0134 37848304 12. ReflectionMethod->invokeArgs() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestCase.php:983
14.0134 37848336 13. Bb\UserBundle\Tests\Security\RoutePermissionCheckerTest->testIndex() /Users/maurits_dekkers/pear/share/pear/PHPUnit/Framework/TestCase.php:983
However, there must be a better way by means of a configuration setting. I've tried adding an extra Monolog setting to my config_test.yml:
# app/config/config_test.yml
monolog:
handlers:
console:
type: console
bubble: false
level: info
but that does not result in PHP errors being reported during my unit test. The unit test simply stops running.
Is there an easy way to configure my Symfony2 project to always report errors during a unit test?
You might want to set the following parameters to true in your PHPUnit configuration file (app/config/phpunit.xml):
convertErrorsToException
convertNoticesToExceptions
convertNoticesToExceptions
Related
Am getting below issue after switching phpunit version from 7.1 to 9.5.28
Failed asserting that exception of type "Error" matches expected exception "Exception".
expectExceptionMessageRegExp() is deprecated.
expectExceptionMessageRegExp() is deprecated in PHPUnit 8 and will be removed in PHPUnit 9. Use expectExceptionMessageMatches() instead.
Example :
function testResponse(){
$this->expectExceptionMessageRegExp("/Unable to emit response/");
}
Replace with :
function testResponse(){
$this->expectExceptionMessageMatches("/Unable to emit response/");
}
this my first question.
I install composer (1.10.5), fatfree-core(3.7) and phpunit (9.1.3) and go to TDD.
All working, but if i use $this->expectOutputString();
I've Assertions: 0 - not working.
I found it's:
> $f3 = Base::instance();
>
> $f3 -> mock ('GET /'); // <= it is problem
<?php
[...]
protected function setUp (): void
{
$this->f3 = Base::instance ();
$this->f3 -> set ('QUIET',TRUE);
$this->f3 -> config ('config.ini');
$this->f3 -> mock ('GET /ftp');
$this -> MainController = new MainController ($this->f3);
}
public function testExpectFooActualFoo ()
{
$this->expectOutputString ('foo');
print'foo';
}
[...]
CMD:
#php ./vendor/phpunit/phpunit/phpunit tests/MainControllerNoStaticTest.php
PHPUnit 9.1.3 by Sebastian Bergmann and contributors.
fooR
1 / 1 (100%)
Time: 00:00.052, Memory: 4.00 MB
There was 1 risky test:
1) MainControllerNoStaticTest::testExpectFooActualFoo
Test code or tested code did not (only) close its own output buffers
OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Risky: 1.
Base heve finall class, before I mocked myself i edit base core and delete finall, but now this not good idea. Base (fat-free) have mock option.
Do you have an idea how to get it to be good?
- without modifying fat-free and phpunit files?
I want to use phpunit for testing, and I put the programming environment on fat-free.
Use getActualOutputForAssertion() to get the output and then use regular assertions on its return value. This will not help you, though, with misbehaving code under test that does not (only) close its own output buffers.
I tried to use PHPUnit v8. However I was not succeeded with PhpStorm. When I run simple test (class method) in PhpStorm I got the following message:
PHP Fatal error: Uncaught PHPUnit\Runner\Exception: Class 'Mrself\\TreeType\\Tests\\Functional\\BuildingTest' could not be found in '/vagrant/symfony-tree-type/tests/Functional/BuildingTest.php'. in /vagrant/symfony-tree-type/vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php:65
Yes, I have that class and yes I have psr configured properly:
"autoload": {
"psr-4": {
"Mrself\\TreeType\\": "./src/"
}
},
"autoload-dev": {
"psr-4": {
"Mrself\\TreeType\\Tests\\": "./tests/"
}
}
The proof the I have everything correctly setup is that when I run vendor/bin/phpunit it gives me correct result.
When I run method in PhpStorm I got the following call:
/usr/bin/php /vagrant/symfony-tree-type/vendor/phpunit/phpunit/phpunit --configuration /vagrant/symfony-tree-type/phpunit.xml --filter "/(::testFormCanBeBuild)( .*)?$/" Mrself\\TreeType\\Tests\\Functional\\BuildingTest /vagrant/symfony-tree-type/tests/Functional/BuildingTest.php --teamcity
However if I prepend class namespace with \\ everything works correctly as well. I can not get a clue what's going on. PHPUnit version 7 works as well.
Same thing happened to me. All of the sudden I started getting the following error:
PHP Fatal error: Uncaught PHPUnit\Runner\Exception: Class 'Tests\\Feature\\ExampleTest' could not be found
And after I have read #frank-vue's comment I noticed the same thing and he did: If I run tests on the entire folder it runs normally, but if I run test on a specific class/method I get that error.
I tried earlier version of PHPStorm, downgraded PHP plugin etc... and nothing worked.
In my case, when I checked the stacktrace looks like:
#0 /var/www/vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php(145): PHPUnit\Runner\StandardTestSuiteLoader->load('Tests\\\\Unit\\\\Ex...', '/var/www/tests/...')
#1 /var/www/vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php(105): PHPUnit\Runner\BaseTestRunner->loadSuiteClass('Tests\\\\Unit\\\\Ex...', '/var/www/tests/...')
#2 /var/www/vendor/phpunit/phpunit/src/TextUI/Command.php(177): PHPUnit\Runner\BaseTestRunner->getTest('Tests\\\\Unit\\\\Ex...', '/var/www/tests/...', Array)
#3 /var/www/vendor/phpunit/phpunit/src/TextUI/Command.php(159): PHPUnit\TextUI\Command->run(Array, true)
#4 /var/www/vendor/phpunit/phpunit/phpunit(61): PHPUnit\TextUI\Command::main()
#5 {main}
thrown in /var/www/vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php on line 69
Notice Tests\\\\Unit\\\\Ex... instead of Tests\\Unit\\Ex....
So in the end I broke the rule and I've modified vendor file, which should be avoided at any cost, but as a temporary solution it solves my problem.
So I added 1 line to the vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php on line 98 (PHPUnit version 8.4.1), which replaces unnecessary '\'s.
if (empty($suiteClassFile) && \is_dir($suiteClassName) && !\is_file($suiteClassName . '.php')) {
/** #var string[] $files */
$files = (new FileIteratorFacade)->getFilesAsArray(
$suiteClassName,
$suffixes
);
$suite = new TestSuite($suiteClassName);
$suite->addTestFiles($files);
return $suite;
}
$suiteClassName = str_replace('\\\\', '\\', $suiteClassName); // THIS IS THE LINE I ADDED
try {
$testClass = $this->loadSuiteClass(
$suiteClassName,
$suiteClassFile
);
} catch (Exception $e) {
$this->runFailed($e->getMessage());
return null;
}
My use-case is this:
I have a static library which I want to be available for some profiles (e.g. "gcc", "arm-gcc", "mips-gcc").
I also have an application which links to this library, but this applications should only build using a specific profile (e.g. "arm-gcc").
For this I am modifying the app-and-lib QBS example.
The lib.qbs file:
import qbs 1.0
Product {
qbs.profiles: ["gcc", "arm-gcc", "mips-gcc"] //I added only this line
type: "staticlibrary"
name: "mylib"
files: [
"lib.cpp",
"lib.h",
]
Depends { name: 'cpp' }
cpp.defines: ['CRUCIAL_DEFINE']
Export {
Depends { name: "cpp" }
cpp.includePaths: [product.sourceDirectory]
}
}
The app.qbs file:
import qbs 1.0
Product {
qbs.profiles: ["arm-gcc"] //I added only this line
type: "application"
consoleApplication: true
files : [ "main.cpp" ]
Depends { name: "cpp" }
Depends { name: "mylib" }
}
The app build fails. Qbs wrongly tries to link to the "gcc" version of the library instead of the "arm-gcc" version, as you can see in the log:
Build graph does not yet exist for configuration 'default'. Starting from scratch.
Resolving project for configuration default
Setting up build graph for configuration default
Building for configuration default
compiling lib.cpp [mylib {"profile":"gcc"}]
compiling lib.cpp [mylib {"profile":"arm-gcc"}]
compiling lib.cpp [mylib {"profile":"mips-gcc"}]
compiling main.cpp [app]
creating libmylib.a [mylib {"profile":"gcc"}]
creating libmylib.a [mylib {"profile":"mips-gcc"}]
creating libmylib.a [mylib {"profile":"arm-gcc"}]
linking app [app]
ERROR: /usr/bin/arm-linux-gnueabihf-g++ -o /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/app.7d104347/app /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/app.7d104347/3a52ce780950d4d9/main.cpp.o /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/mylib.eyJwcm9maWxlIjoiZ2NjIn0-.792f47ec/libmylib.a
ERROR: /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/mylib.eyJwcm9maWxlIjoiZ2NjIn0-.792f47ec/libmylib.a: error adding symbols: File format not recognized
collect2: error: ld returned 1 exit status
ERROR: Process failed with exit code 1.
The following products could not be built for configuration default:
app
The build fails only when selecting one profile in the app.qbs file, and this profile should not be the first profile in the qbs.profiles line in the lib.qbs file.
When selecting two or more profiles - the build succeeds.
My analysis:
I think this problem is related to multiplexing:
The lib.qbs contains more than one profile. This turns on multiplexing when building the library, which, in turn, adds additional 'multiplexConfigurationId' to the build-directory name (moduleloader.cpp).
The app.lib contains only one profile, so multiplexing is not turned on and the build-directory name does not get the extra string.
The problem can be solved by changing the code (moduleloader.cpp) so that multiplexing is turned even if there is only one profile i.e. with the following patch:
--- moduleloader.cpp 2018-10-24 16:17:43.633527397 +0300
+++ moduleloader.cpp.new 2018-10-24 16:18:27.541370544 +0300
## -872,7 +872,7 ##
= callWithTemporaryBaseModule<const MultiplexInfo>(dummyContext,
extractMultiplexInfoFromProduct);
- if (multiplexInfo.table.size() > 1)
+ if (multiplexInfo.table.size() > 0)
productItem->setProperty(StringConstants::multiplexedProperty(), VariantValue::trueValue());
VariantValuePtr productNameValue = VariantValue::create(productName);
## -891,7 +891,7 ##
const QString multiplexConfigurationId = multiplexInfo.toIdString(row);
const VariantValuePtr multiplexConfigurationIdValue
= VariantValue::create(multiplexConfigurationId);
- if (multiplexInfo.table.size() > 1 || aggregator) {
+ if (multiplexInfo.table.size() > 0 || aggregator) {
multiplexConfigurationIdValues.push_back(multiplexConfigurationIdValue);
item->setProperty(StringConstants::multiplexConfigurationIdProperty(),
multiplexConfigurationIdValue);
This worked for my use case. I don't know if it make sense in a broader view.
Finally, the questions:
Does it all make sense?
Is this a normal behavior?
Is this use-case simply not supported?
Is there a better solution?
Thanks in advance.
Yes, the default behavior with multiplexing is that the a non-multiplexed product depends on all variants of the dependency. In general, there is no way for a user to change that behavior, but there should be.
However, luckily for you, profiles are special:
Depends { name: "mylib"; profiles: "arm-gcc" }
This should fix your problem.
using the classical phpunit style I generate:test with the symfony2 helper.
I can get the service and I can assert it ok.
// tests
public function testGetServiceUrl() {
$ecservice = $this->getModule('Symfony2')->grabServiceFromContainer("ecservice");
$this->assertEquals("https://ecoconnect2.niwa.co.nz/services", $ecservice->getServiceUrl());
$this->assertEquals("xxx", $ecservice->getServiceUrl());
}
However in the second case where the assertion fails I get an exception:
Trying to test get service url (demoTest::testGetServiceUrl) - Failed
PHP Fatal error: Call to a member function getResponse() on a non-object in /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/Util/Framework.php on line 30
PHP Stack trace:
PHP 1. {main}() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/codecept:0
PHP 2. Symfony\Component\Console\Application->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/codecept:38
PHP 3. Symfony\Component\Console\Application->doRun() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:106
PHP 4. Symfony\Component\Console\Command\Command->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:193
PHP 5. Codeception\Command\Run->execute() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:240
PHP 6. Codeception\Codecept->runSuite() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/Command/Run.php:74
PHP 7. Codeception\SuiteManager->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/Codecept.php:110
PHP 8. Codeception\PHPUnit\Runner->doEnhancedRun() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/SuiteManager.php:132
PHP 9. PHPUnit_Framework_TestSuite->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/PHPUnit/Runner.php:107
PHP 10. PHPUnit_Framework_TestSuite->runTest() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php:745
PHP 11. PHPUnit_Framework_TestCase->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php:775
PHP 12. PHPUnit_Framework_TestResult->run() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php:776
PHP 13. PHPUnit_Framework_TestResult->addFailure() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php:698
PHP 14. Codeception\PHPUnit\Listener->addFailure() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php:307
PHP 15. Codeception\PHPUnit\Listener->fire() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/PHPUnit/Listener.php:24
PHP 16. Symfony\Component\EventDispatcher\EventDispatcher->dispatch() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/PHPUnit/Listener.php:66
PHP 17. Symfony\Component\EventDispatcher\EventDispatcher->doDispatch() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php:53
PHP 18. call_user_func() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php:164
PHP 19. Codeception\Subscriber\Module->failed() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/EventDispatcher.php:164
PHP 20. Codeception\Util\Framework->_failed() /Users/watkinsav/workspace/cd/ecoconnect_web/vendor/codeception/codeception/src/Codeception/Subscriber/Module.php:47
This is because the _failed() function in the Framework.php tries to call client-getResponse(). And we don't have a response class instantiated.
public function _failed(\Codeception\TestCase $test, $fail)
{
if (!$this->client->getResponse()) return;
file_put_contents(\Codeception\Configuration::logDir() . basename($test->getFileName()) . '.page.debug.html', $this->client->getResponse()->getContent());
}
In the example blog post here: http://codeception.com/02-12-2013/testing-symfony2.html There are some extra lines - but they do not fix the problem.
Thanks again Andrew
Well, looks like this function expected to get null from getResponse.
if (!$this->client->getResponse()) return;
This line should have blocked the screenshot call if no response is available.
However it didn't. Looks like an issue. Which Symfony version do you use?