I need to run a couple of tests on a console command, each of which will have a small change to the database before the test is run. I've structured this as follows:
class ActionRemindCommandEmail extends KernelTestCase
{
private $mailCollector;
/**
* #return mixed
*/
public function getMailCollector()
{
return $this->mailCollector;
}
/**
* #param mixed $mailCollector
*/
public function setMailCollector($mailCollector)
{
$this->mailCollector = $mailCollector;
}
protected function setUp()
{
exec('mysql database < prep.sql');
}
public function testExecute()
{
exec('echo "' . str_replace('"', '\\"', $this->getPrepSql() ) . '" | mysql database');
self::bootKernel();
$application = new Application(self::$kernel);
$application->add(new ActionRemindCommand());
$client = static::createClient();
$client->enableProfiler();
$this->setMailCollector($client->getProfile()->getCollector('swiftmailer'));
$command = $application->find('app:ahp:remind');
$commandTester = new CommandTester($command);
$commandTester->execute(array(
'command' => $command->getName(),
'email' => ''
));
$output = $commandTester->getDisplay();
$this->assertions();
}
}
and the first actual test looks like this
class ActionRemindCommandVetNotifyWhenSavedOnlyTest extends ActionRemindCommandEmailTest
{
protected function getPrepSql()
{
return "INSERT INTO `action` (farmer_id`, `group_id`, `name`, `due`, `active`, `completed`, `notify_vet_email`, `notify_farmer_email`, `notify_vet_text`, `notify_farmer_text`, `notify_tech_text`, `notify_clinic_email`, `notify_farmer_text2`, `notified_vet_email`, `notified_farmer_email`, `notified_clinic_email`, `notified_vet_text`, `notified_farmer_text`, `notified_farmer_text2`, `notified_tech_text`, `notify_vet_email_advance`, `notify_farmer_email_advance`, `notify_clinic_email_advance`, `notify_vet_text_advance`, `notify_farmer_text_advance`, `notify_farmer_text2_advance`, `notify_tech_text_advance`, `no_time`, `notes`, `pending_offline_id`) VALUES "
. " (116, NULL, 'Action 1', '"
. date('Y-m-d H:i:s',mktime(12,0,0,intval(date('n')), intval(date('j'))+14,intval(date('Y'))))
. "', 1, 0, 1, 0, 0, 0, 0, 0, 0, '000', '000', '000', '000', '000', '000', '000', -1, -1, -1, 24, 24, 24, 24, 0, 'undefined', 'N1478139417184')";
}
public function testExecute()
{
parent::testExecute();
}
protected function assertions()
{
$this->assertEquals(1, $this->getMailCollector()->getMessageCount());
}
}
Setting this up in phpstorm with app/phpunit.xml.dist:
<?xml version="1.0" encoding="UTF-8"?>
<!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="bootstrap.php.cache"
>
<testsuites>
<testsuite name="Project Test Suite">
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/*/Bundle/*Bundle/Tests</directory>
</testsuite>
</testsuites>
<!--
<php>
<server name="KERNEL_DIR" value="/path/to/your/app/" />
</php>
-->
<filter>
<whitelist>
<directory>../src</directory>
<exclude>
<directory>../src/*/*Bundle/Resources</directory>
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/*/Bundle/*Bundle/Resources</directory>
<directory>../src/*/Bundle/*Bundle/Tests</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
I find that I cannot execute the test, because "test class is not specified or invalid"
I tried using scope: class, method, but I'm not sure what else could be missing?
As specified in PHPUnit docs:
If you point the PHPUnit command-line test runner to a directory it will look for *Test.php files.
Meaning that by default PHPUnit considers only classes ending with Test.php.
Expected test suffix can be changed in phpunit.xml.
Looks like this:
<directory suffix=".test">./sourceFolder</directory>
See this good answer for more details.
Adding Test as a suffix to the Parent class solved the problem.
Related
Version :
Symfony 3.4
phpunit 8.3
My phpunit.xml file :
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="autoload.php">
<php>
<ini name="error_reporting" value="-1" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
<!--
<server name="KERNEL_DIR" value="/path/to/your/app/" />
-->
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/*/Bundle/*Bundle/Tests</directory>
<directory>../src/*Bundle/Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>../src</directory>
<exclude>
<directory>../src/*Bundle/Resources</directory>
<directory>../src/*Bundle/Tests</directory>
<directory>../src/*/*Bundle/Resources</directory>
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/*/Bundle/*Bundle/Resources</directory>
<directory>../src/*/Bundle/*Bundle/Tests</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
my test File :
class QuittancementControllerTest extends WebTestCase
{
private $client;
/** #var EntityManager */
private $em;
private $myApiUrl;
/** #var BailRepository */
private $bailRepo;
/** #var EcritureCompteLocataireRepository */
private $ecritureRepo;
public function setUp(): void
{
$this->client = static::createClient([], [
'PHP_AUTH_USER' => 'testUser',
'PHP_AUTH_PW' => 'pa$$word',
]);
$this->myApiUrl = $this->client->getContainer()->getParameter('myApi_url');
$this->client->disableReboot();
$this->em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$this->em->beginTransaction();
$this->ecritureRepo = $this->em->getRepository('AppBundle:EcritureCompteLocataire');
$this->bailRepo = $this->em->getRepository('AppBundle:Bail');
}
public function run(TestResult $result = null): TestResult
{
$this->setPreserveGlobalState(false);
return parent::run($result);
}
public function tearDown(): void
{
$this->em->rollback();
}
/**
* Vérifie que l'insertion d'une écriture à la date du jour met bien à jour le solde des écritures qui suivent
* #runInSeparateProcess
*/
public function testMajSoldeEcritureFuture(): void
{
/** #var Bail $bail */
$bail = $this->bailRepo->findOneBy(['numeroCompteTiers' => '9000001']);
$postData = array
(
'bail' => $bail->getId(),
'test' => 'example',
);
$this->client->request(Request::METHOD_POST, $this->myApiUrl.'/api/test', $postData);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$solde = $this->ecritureRepo->getSolde($bail->getId(), new DateTime());
// voluntary false assertion
$this->assertEquals(111, 10);
}
}
If i run the test like this, i have this error :
{"code":500,"message":"Serialization of 'SimpleXMLElement' is not allowed"}
C:\wamp64\www\chronos2017\src\tests\AppBundle\Controller\QuittancementControllerTest.php:44
assertion with statut 200 is write and the second assertion is false but phpunit can't display properly the result
If i delete the line "#runInSeparateProcess", the client call fails :
{"code":500,"message":"Failed to start the session because headers have already been sent by \"C:\wamp64\www\mySite\src\vendor\phpunit\phpunit\src\Util\Printer.php\" at line 109."}
What's the right way to do functional tests with symfony ? Can you help me ?
I use symfony 3.4 also but phpunit 7 so maybe it's because of phpunit 8, but I didn't need to make any modifications to my phpunit.xml file. And your error seems to come from your xml file according to the error.
I guess you already used documentation but if not: https://symfony.com/doc/3.4/testing.html
My file:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="phpunit.xsd"
bootstrap="tests/bootstrap.php"
cacheResult="true"
verbose="true">
<testsuites>
<testsuite name="unit">
<directory suffix="Test.php">tests/unit</directory>
</testsuite>
<testsuite name="end-to-end">
<directory suffix=".phpt">tests/end-to-end</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
<exclude>
<file>src/Framework/Assert/Functions.php</file>
<file>src/Util/PHP/eval-stdin.php</file>
</exclude>
</whitelist>
</filter>
<php>
<const name="PHPUNIT_TESTSUITE" value="true"/>
</php>
</phpunit>
I am trying to create and run PHPUnit tests for Drupal 8. Here are the details:
My top-level directory, where composer.json is located.
Dockerfile bootstrap.php composer.lock phpunit-examples phpunit.xml.org web
Jenkinsfile checkstyle.xml config phpunit.xml scripts
LICENSE components drush phpunit.xml.dist sonar-project.properties
README.md composer.json patches phpunit.xml.dist.org vendor
./vendor/bin/phpunit --version
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.
phpunit.xml.dist
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
verbose="true"
>
<testsuites>
<testsuite name="unit">
<file>./tests/TestSuites/UnitTestSuite.php</file>
</testsuite>
<testsuite name="kernel">
<file>./tests/TestSuites/KernelTestSuite.php</file>
</testsuite>
<testsuite name="functional">
<file>./tests/TestSuites/FunctionalTestSuite.php</file>
</testsuite>
<testsuite name="functional-javascript">
<file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file>
</testsuite>
</testsuites>
</phpunit>
phpunit.xml
<phpunit
bootstrap="bootstrap.php"
colors="true"
strict="true"
verbose="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
checkForUnintentionallyCoveredCode="false">
<testsuites>
<testsuit name="Simple Example Test Suite">
<directory>phpunit-examples/tests</directory>
</testsuit>
</testsuites>
<php>
<ini name="error_reporting" value="32767"/>
<ini name="memory_limit" value="-1"/>
<env name="SIMPLETEST_BASE_URL" value="http://drupal-8.localhost"/>
<env name="SIMPLETEST_DB" value="mysql://drupal-8:drupal-8#localhost/drupal-8"/>
<env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/var/www/sites/default/simpletest"/>
<includePath>phpunit-examples/src/</includePath>
</php>
</phpunit>
custom code to be tested:
web/modules/custom/benefit/src/BenefitListBuilder.php
test located at:
web/modules/custom/benefit/tests/src/BenefitListBuilderTest.php
<?php declare(strict_types = 1);
namespace Drupal\Tests\benefit;
use Mockery;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;
use Drupal\benefit\BenefitListBuilder;
/**
* Test basic functionality of My Module.
*
* #group benefit
*/
class BenefitListBuilderTest extends UnitTestCase
{
/** #var BenefitListBuilder */
private $benefitListBuilder;
protected function setUp()
{
$a = "var_a";
$b = "var_b";
$this->benefitListBuilder = new BenefitListBuilder($a,$b);
}
public function testMissing()
{
$this->fail('Test not yet implemented');
}
}
Now, i try to run just this test:
$./vendor/bin/phpunit web/modules/custom/benefit/tests/src/BenefitListBuilderTest.php
PHP Fatal error: Class 'Drupal\Tests\benefit\UnitTestCase' not found in /Users/syedahmed/BG-REPOS/PHPUNITTEST-BenefitsAPI/BenefitsAPI/web/modules/custom/benefit/tests/src/BenefitListBuilderTest.php on line 15
Fatal error: Class 'Drupal\Tests\benefit\UnitTestCase' not found in /Users/syedahmed/BG-REPOS/PHPUNITTEST-BenefitsAPI/BenefitsAPI/web/modules/custom/benefit/tests/src/BenefitListBuilderTest.php on line 15
i tried moving the test under web/modules/custom/benefit/tests/src/Unit/BenefitListBuilderTest.php , but got same error.
How do i get the test to recognize the Path for UnitTestCase?
Update:
I have setup the repository in PHPStorm, so now i am getting error:
Error : Class 'Drupal\Tests\BenefitListBuilder' not found
As I see it, #kamal, the problem is you're instantiating the tests as this: use PHPUnit\Framework\TestCase; but you're using Drupal, so it should be as follows: use Drupal\Tests\UnitTestCase;
I hope this helps.
I am working on a Symfony 3.4 installation that was upgraded from version 2.
When I try to run a unit test with this command
./bin/phpunit tests/MyBundle/ -c tests/phpunit.xml
I get the following error:
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "XXXX\MyBundle\Services\ListServices".
/apps/xxxx/unitTest/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php:348
/apps/xxxx/unitTest/tests/MyBundle/Services/ListServicesTest.php:19
I have made the Services public
services:
_defaults:
autowire: true
autoconfigure: true
public: true
But that did not work. I also tried adding a Compiler pass, but that was it's own can of worms...
my phpunit.xml
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
convertErrorsToExceptions = "true"
convertNoticesToExceptions = "true"
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
stderr = "true"
bootstrap="bootstrap_test.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="KERNEL_CLASS" value="AppKernel" />
<server name="KERNEL_DIR" value="../app/" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/>
<env name="APP_ENV" value="test"/>
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>../tests/*</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>../src</directory>
<exclude>
<directory>../src/*/*Bundle/Resources</directory>
<directory>../src/*/*Bundle/Tests</directory>
<directory>../src/*/*Bundle/DataFixtures</directory>
<directory>../src/*/*Bundle/Admin</directory>
<directory>../src/*/*Bundle/DependencyInjection</directory>
<directory>../src/*/*Bundle/Twig</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
this is my test file
namespace Tests\MyBundle\Services;
use XXXX\MyBundle\Services\ListServices;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ListServicesTest extends WebTestCase
{
/**
* #test
*/
public function testGetCurrentList()
{
$kernel = new AppKernel('dev', false);
$kernel->boot();
$container = $kernel->getContainer();
$listService = $container->get(ListServices::class);
$this->assertNotNull($listService->getCurrentList());
}
}
Any ideas on how to get the unit tests to work?
thanks
You are trying to get the service by it's name (class name) and not by it's alias from the container. Service should be registered in the container:
services:
your_service_alias:
class: YourApp\Services\ListServices
arguments: (if any)
- "#some_dependency"
Then in your test you should get the service by it's alias
$container->get('your_service_alias')
I face a problem with env variables and running tests with phpunit on symfony 4.2 framework.
According to the documentation, .env and services.yaml are loaded first but if the APP_ENV is defined and set to a value like test, the container will be configured to load .env.test and services_test.yaml.
What I want to understand is : my .env files contains a key APP_ENV set to dev, but when I run phpunit to execute some tests, the environment is automatically (?) set to test...
To see that, I've dump $this->environment in the class Kernel.php.
bootstrap.php
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
// Load cached env vars if the .env.local.php file exists
// Run "composer dump-env prod" to create it (requires symfony/flex >=1.2)
if (is_array($env = #include dirname(__DIR__).'/.env.local.php')) {
$_SERVER += $env;
$_ENV += $env;
} elseif (!class_exists(Dotenv::class)) {
throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
} else {
// load all the .env files
(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');
}
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';
.env
###> symfony/framework-bundle ###
APP_ENV=dev
...
Kernel.php
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
{
$container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
// Feel free to remove the "container.autowiring.strict_mode" parameter
// if you are using symfony/dependency-injection 4.0+ as it's the default behavior
$container->setParameter('container.autowiring.strict_mode', true);
$container->setParameter('container.dumper.inline_class_loader', true);
$confDir = $this->getProjectDir().'/config';
$loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
dump($this->environment); //print "test"
}
phpunit.xml.dist
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="config/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="KERNEL_CLASS" value="AppBundle\Kernel" />
<!-- ###+ symfony/framework-bundle ### -->
<env name="APP_ENV" value="dev" force="true"/> <!-- doesn't work with or without force="true" -->
<!-- env name="TRUSTED_PROXIES" value="127.0.0.1,127.0.0.2" -->
<!-- env name="TRUSTED_HOSTS" value="'^localhost|example\.com$'" -->
<!-- ###- symfony/framework-bundle ### -->
<!-- ###+ doctrine/doctrine-bundle ### -->
<!-- Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url -->
<!-- For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" -->
<!-- Configure your db driver and server_version in config/packages/doctrine.yaml -->
<env name="DATABASE_URL" value="sqlite:///%kernel.project_dir%/app/test.db"/>
<!-- ###- doctrine/doctrine-bundle ### -->
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src</directory>
<exclude>
<directory>src/*Bundle/Resources</directory>
<directory>src/*/*Bundle/Resources</directory>
<directory>src/*/Bundle/*Bundle/Resources</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
Could you give me a clue about what happens, why my container is configure in test environment instead of the value of the APP_ENV?
I'm trying to print a coloured table after running a Symfony2 CLI command which works fine when running on its own like this:
CLI Command:
php app/console phing:report inanzzz
Output:
PROBLEM:
When I run same CLI command within Phing, table colour is being overridden as seen below. Anyone knows a solution to it?
CLI Command:
bin/phing build-report
Output:
Phing entry for same CLI command:
<target name="build-report">
<echo msg="Generating final build report ..." />
<exec logoutput="true" checkreturn="true" command="php app/console phing:report inanzzz" dir="./" />
</target>
CLI Command Class:
namespace Site\FrontendBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
class PhingCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('phing:report')
->addArgument('path', InputArgument::IS_ARRAY, 'Path argument is missing!'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$path = $input->getArgument('path');
$message[] = array(
'<fg=green>Behat</fg=green>',
'<fg=green>behat-2014.html</fg=green>',
'<fg=green>0</fg=green>',
'<fg=green>Success</fg=green>'
);
$message[] = array(
'<fg=green>Copy Paste Detector</fg=green>',
'<fg=green>phpcpd-2014.html</fg=green>',
'<fg=green>0</fg=green>',
'<fg=green>Success</fg=green>'
);
$message[] = array(
'<fg=red>Codesniffer</fg=red>',
'<fg=red>phpcs-2014.html</fg=red>',
'<fg=red>4</fg=red>',
'<fg=red;options=blink>Fail</fg=red;options=blink>'
);
$message[] = array(
'<fg=yellow>Mess Detector</fg=yellow>',
'<fg=yellow>phpmd-2014.html</fg=yellow>',
'<fg=yellow>14</fg=yellow>',
'<fg=yellow>Warning</fg=yellow>'
);
$table = new Table($output);
$table
->setHeaders(
array(
'<fg=white;options=bold>TEST</fg=white;options=bold>',
'<fg=white;options=bold>LOG FILE</fg=white;options=bold>',
'<fg=white;options=bold>ERROR COUNT</fg=white;options=bold>',
'<fg=white;options=bold>STATUS</fg=white;options=bold>'
)
)
->setRows($message)
;
$table->render();
}
}
Try adding passthru="true" attribute to your <exec/> element:
<target name="build-report">
<echo msg="Generating final build report ..." />
<exec passthru="true" logoutput="true" checkreturn="true" command="php app/console phing:report inanzzz" dir="./"/>
</target>