How to setup which Symfony environment is used by unit tests? - symfony

I am trying to setup a new Symfony environment named travis to run unit tests in a Travis container.
I setup this environment to distinguish it from prod and from dev.
Currently, I have:
a SYMFONY_ENV=travis environment variable setup in Travis
a config_travis.yml that contains my configuration for the Travis environment
a app_travis.php which specify the environment to load
a .travis.yml:
>
language: php
php:
- "7.2.17"
services:
- mysql
install:
- composer install --no-interaction
- echo "USE mysql;\nUPDATE user SET password=PASSWORD('${MYSQL_PASSWORD}') WHERE user='root';\nFLUSH PRIVILEGES;\n" | mysql -u root
- ./bin/console doctrine:database:create --env=travis
- ./bin/console doctrine:migration:migrate --env=travis --no-interaction
script:
- ./vendor/bin/simple-phpunit
My project looks like this:
Some examples of tests I'm running:
UserTest.php which tests the User.php model:
<?php
namespace Tests\AppBundle\Entity;
use AppBundle\Entity\User;
use PHPUnit\Framework\TestCase;
use AppBundle\Entity\Responsibility;
class UserTest extends TestCase
{
public function testId()
{
$user = new User();
$id = $user->getId();
$this->assertEquals(-1, $id);
}
}
LoginControllerTest.php which tests the LoginController.php controller:
<?php
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\HttpFoundation\Response;
class LoginControllerTest extends WebTestCase
{
/*
* Test the login form
* Logins with (admin, password : a)
*/
public function testLogin()
{
// Create a new client to browse the app
$client = static::createClient();
$crawler = $client->request('GET', '/login');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET ");
// Get the form
$form = $crawler->selectButton('Connexion')->form();
// Fill the login form input
$form['_username']->setValue('admin');
$form['_password']->setValue('a');
// Send the form
$client->submit($form);
$crawler = $client->followRedirect();
$this->assertContains(
'Bienvenue admin.' ,
$client->getResponse()->getContent()
);
return array($client,$crawler);
}
}
My problem is: all the command run into the travis environment, except the unit tests. I want to be able to run the unit tests in dev env on my computer but in travis env in the Travis container.
How can I setup my PHPUnit so that it can run in travis environment and use my config_travis.yml file?

The createClient() method of the WebTestCase calls the bootKernel() method from the KernelTestCase which in turn calls createKernel(). In createKernel() there is the following code which determines in which environment the kernel should be booted:
if (isset($options['environment'])) {
$env = $options['environment'];
} elseif (isset($_ENV['APP_ENV'])) {
$env = $_ENV['APP_ENV'];
} elseif (isset($_SERVER['APP_ENV'])) {
$env = $_SERVER['APP_ENV'];
} else {
$env = 'test';
}
So in your case exporting the APP_ENV variable in your config_travis.yml file and setting it to travis should solve it.

PHPUnit uses an environment variable called APP_ENV to determines which environment is used. I had to create this environment variable in Travis.

Related

Symfony 4 disable profiler in controller action

I have an action in the controller for mass instert in the database...
So this uses a lot of resources and the profiler is caching everything and server goes down.
How can i disable the profiler (and all the debug services) in one action on the controller?
The controller looks like :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use App\Sync\Incomming\Syncronize;
/**
* #Route("/sync")
*/
class SyncController extends AbstractController
{
private $syncronize;
public function __construct(Syncronize $syncronize)
{
$this->syncronize = $syncronize;
}
/**
* #Route("/",name="sync_index")
*/
public function index(Request $request, Profiler $profiler)
{
$message = "Hello";
return $this->render( 'sync/output.html.twig', ['message' => $message ]);
}
}
if I try to autowire the profiler in the constructor method then I get the error public function __construct(Syncronize $syncronize, Profiler $profiler):
Cannot autowire service "App\Controller\SyncController": argument
"$profiler" of method "__construct()" references class
"Symfony\Component\HttpKernel\Profiler\Profiler" but no such service
exists. You should maybe alias this class to the existing "profiler"
service.
if I try to autowire the profiler in the index method then I get the error public function index(Request $request, Profiler $profiler):
Controller "App\Controller\SyncController::index()" requires that you
provide a value for the "$profiler" argument. Either the argument is
nullable and no null value has been provided, no default value has
been provided or because there is a non optional argument after this
one.
EDIT
For big queries disabling the profiler was not the solution... Actually you need to disable the setSQLLogger:
$em->getConnection()->getConfiguration()->setSQLLogger(null);
Symfony 3.4 / 4
https://symfony.com/doc/4.0/profiler/matchers.html
use Symfony\Component\HttpKernel\Profiler\Profiler;
class DefaultController
{
// ...
public function someMethod(Profiler $profiler)
{
// for this particular controller action, the profiler is disabled
$profiler->disable();
// ...
}
}
If you have an error with autowiring
# config/services.yaml
services:
Symfony\Component\HttpKernel\Profiler\Profiler: '#profiler'
Old:
If you want to disable the profiler from a controller, you can like this:
use Symfony\Component\HttpKernel\Profiler\Profiler;
// ...
class DefaultController
{
// ...
public function someMethod(Profiler $profiler)
{
// for this particular controller action, the profiler is disabled
$profiler->disable();
// ...
}
}
Source: https://symfony.com/doc/current/profiler/matchers.html
Another way would be to use: $this->get('profiler')->disable();
Older:
Simply switch the app to prod mode and disable debug mode.
To do this, open the .env file on the server in your favourite editor (Note: You should never commit this file to Git, as you store secrets in there!).
In that file, you should see a section starting with: ###> symfony/framework-bundle ###
Just below that there is a APP_ENV=dev and APP_DEBUG=1, change these two lines to APP_ENV=prod and APP_DEBUG=0. Then save the file.
Next you should clear the cache for prod mode and install the assets. To do this, run the following commands:
php bin/console cache:clear --env=prod --no-debug
php bin/console cache:warmup --env=prod --no-debug
php bin/console assets:install --env=prod --no-debug --symlink
If you now load the application, it is in prod mode, which includes more caching and is faster as debug is disabled.
Note:
There will still be a timelimit for PHP. If you still hit that limit, you can either change your PHP setting or alternatively you could run the import from CLI, as CLI usually has no timelimit. If users need to be able to upload on their own, I'd suggest having them upload the file, enter a "note" about the file to a db and have a cronjob reading that db for not imported files and import them.

Symfony2: How do I clear all of Symfony's (and all 3rd party plugins) cache in 1 command?

so I've been testing Doctrine queries and other Symfony code and I have to run several commands just to clear Doctrine/Symfony caches. I was searching for the net and came across another command to clear Assetic assets/etc.
From what I've read
php app/console cache:clear
will only clear Symfony cache. it won't include Doctrine and perhaps more.
I know I can create a bash script to clear all my caches but this obviously means I know all the "clear cache" commands. I only found out about the Assetic clear cache/assets by accident. What about those I don't know?
So is there 1 "clear cache" command that can do it for me? This will have to include Symfony/Doctrine/Assetic/Twig and whatever plugins I have installed.
Thanks a lot
What you are looking for is highly dependent on the developer of the bundle that uses the cache. Not even doctrine, that comes with the standard version of symfony has its cache clear command integrated. But what you can do is extend the default symfony command with a listener that runs all the cache clear command you want like this:
<?php
namespace DefaultBundle\Event\Listener;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Process\Process;
class CacheClearListener implements CacheClearerInterface
{
private $environment;
/**
* #return array
*/
private static function getCommands()
{
return array(
'php ./app/console doctrine:cache:clear-metadata --no-debug --flush',
'php ./app/console doctrine:cache:clear-query --no-debug --flush',
'php ./app/console doctrine:cache:clear-result --no-debug --flush'
);
}
public function clear($cacheDir)
{
$output = new ConsoleOutput();
$output->writeln('');
$output->writeln('<info>Clearing Doctrine cache</info>');
foreach (self::getCommands() as $command) {
$command .= ' --env='.$this->environment;
$success = $this->executeCommand($command, $output);
if (!$success) {
$output->writeln(sprintf('<info>An error occurs when running: %s!</info>', $command));
exit(1);
}
}
}
/**
* #param string $command
* #param ConsoleOutput $output
*
* #return bool
*/
private function executeCommand($command, ConsoleOutput $output)
{
$p = new Process($command);
$p->setTimeout(null);
$p->run(
function ($type, $data) use ($output) {
$output->write($data, false, OutputInterface::OUTPUT_RAW);
}
);
if (!$p->isSuccessful()) {
return false;
}
$output->writeln('');
return true;
}
/**
* #param Kernel $kernel
*/
public function setKernel(Kernel $kernel)
{
$this->environment = $kernel->getEnvironment();
}
}
Register the listener like this:
<service id="cache_clear_listener" class="DefaultBundle\Event\Listener\CacheClearListener">
<call method="setKernel">
<argument type="service" id="kernel"/>
</call>
<tag name="kernel.cache_clearer" priority="254" />
</service>
And that is all. Now all you need to do is keep adding your new cache clear command to the getCommands() method. In order to find this commands you can run something like
php app/console | grep cache
to see all available commands that contain the word "cache" in them
After your listener is set, every time you run php app/console cache:clear it will trigger all the command that you listed in the getCommands() method of your listener.
Hope this helps,
Alexandru

Symfony2 custom console command

I am still pretty new to Symfony. I have set up 'n demonstration of some of the components that I have written on my online portfolio and I want this demo data to be cleared every two hours. On my web server I want to set a cron job like so:
php app/console portfolio:wipe
I have created app/src/MyFreelancer/PortfolioBundle/Command/WipeCommand.php (PortfolioBundle is registered in AppKernel.php) and here is its contents (copied exactly from http://symfony.com/doc/current/cookbook/console/console_command.html and changed the namespace and command name).
<?php
namespace MyFreelancer\PortfolioBundle\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;
class WipeCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('maintenance:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?')
->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
if ($name) {
$text = 'Hello '.$name;
} else {
$text = 'Hello';
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
}
?>
However, when I run
php app/console portfolio:wipe test
Instead of getting "Hello test", I get
There are no commands defined in the "portfolio" namespace.
Any help would be appreciated.
Your command name is maintenance:greet, so try to call it with php app/console maintenance:greet test
And for your cron job, don't forget to change to the Symfony2 directory before calling php app/console. You can also call the console with the full path : php /var/www/where/is/symfony/app/console ...

The Console Component in Symfony

I want to run a task using console. I checked http://symfony.com/doc/2.0/components/console/introduction.html
It asks to create GreetCommand.php.
namespace Acme\DemoBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends Command
{
protected function configure()
{
$this
->setName('demo:greet')
->setDescription('Greet someone')
->addArgument(
'name',
InputArgument::OPTIONAL,
'Who do you want to greet?'
)
->addOption(
'yell',
null,
InputOption::VALUE_NONE,
'If set, the task will yell in uppercase letters'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
if ($name) {
$text = 'Hello '.$name;
} else {
$text = 'Hello';
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
}
and create another file to run the command as given below.
#!/usr/bin/env php
# app/console
<?php
use Acme\DemoBundle\Command\GreetCommand;
use Symfony\Component\Console\Application;
$application = new Application();
$application->add(new GreetCommand);
$application->run();
But the command to run it is like app/console demo:greet Fool
The thing I won't understand is that why we need to create the second file?
Sometimes, I feel Symfony is the most difficult framework to learn.
In first file you have defined your Command class.
Second file is needed to register/initialize instance of that command. You just tell there that your application will have GreetCommand with name "demo:greet" (name defined in command itself).
BTW When you use full-stack Symfony2 with FrameworkBundle you do not have to create second file (if we follow Symfony2 conventions) cause Command is registered automatically by FrameworkBundle Console Application using HttpKernel component

Symfony2 custom console command not working

I created a new Class in src/MaintenanceBundle/Command, named it GreetCommand.php and put the following code in it:
<?php
namespace SK2\MaintenanceBundle\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;
class GreetCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('maintenance:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?')
->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
if ($name) {
$text = 'Hello '.$name;
} else {
$text = 'Hello';
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
}
?>
And tried to call it via
app/console maintenance:greet Fabien
But i always get the following error:
[InvalidArgumentException]
There are no commands defined in the "maintenance" namespace.
Any ideas?
I had this problem, and it was because the name of my PHP class and file didn't end with Command.
Symfony will automatically register commands which end with Command and are in the Command directory of a bundle. If you'd like to manually register your command, this cookbook entry may help: http://symfony.com/doc/current/cookbook/console/commands_as_services.html
I had a similar problem and figured out another possible solution:
If you override the default __construct method the Command will not be auto-registered by Symfony, so you have to either take the service approach as mentioned earlier or remove the __construct override and make that init step in the execute method or in the configure method.
Does actually anyone know a good best practice how to do init "stuff" in Symfony commands?
It took me a moment to figure this out.
I figured out why it was not working: I simply forgot to register the Bundle in the AppKernel.php. However, the other proposed answers are relevant and might be helpful to resolve other situations!
By convention: the commands files need to reside in a bundle's command directory and have a name ending with Command.
in AppKernel.php
public function registerBundles()
{
$bundles = [
...
new MaintenanceBundle\MaintenanceBundle(),
];
return $bundles;
}
In addition to MonocroM's answer, I had the same issue with my command and was silently ignored by Symfony only because my command's constructor had 1 required argument.
I just removed it and call the parent __construct() method (Symfony 2.7) and it worked well ;)
If you are over-riding the command constructor and are using lazy-loading/autowiring, then your commands will not be automatically registered. To fix this you can add a $defaultName variable:
class SunshineCommand extends Command
{
protected static $defaultName = 'app:sunshine';
// ...
}
Link to the Symfony docs.
I think you have to call parent::configure() in your configure method
I had this same error when I tried to test my command execution with PHPUnit.
This was due to a wrong class import :
use Symfony\Component\Console\Application;
should be
use Symfony\Bundle\FrameworkBundle\Console\Application;
cf. Other stack thread
In my case it was complaining about the "workflow" namespace although the WorkflowDumpCommand was correctly provided by the framework.
However, it was not available to run because I have not defined any workflows so the isEnabled() method of the command returned false.
I tried to use a service passed via constructor inside the configure method:
class SomeCommand extends Command {
private $service;
public function __construct(SomeService $service) {
$this->service = $service;
}
protected function configure(): void {
$this->service->doSomething(); // DOES NOT WORK
}
}
Symfony uses Autoconfiguration that automatically inject dependencies into your services and register your services as Command, event,....
So first just make sure that you have services.yaml in your config folder. with autoconfigure:true.
this is the default setting
Then Make sure That All your files are exactly the same name as Your Class.
so if you have SimpleClass your file must be SimpleClass.php
If you have a problem because of a __constructor,
go to services.yml and add something like this:
app.email_handler_command:
class: AppBundle\Command\EmailHandlerCommand
arguments:
- '#doctrine.orm.entity_manager'
- '#app.email_handler_service'
tags:
- { name: console.command }
For newer Symfony-Version (5+) commands must be registered as services.
What I do frequently forget while setting it up, is to tag it properly:
<service id="someServiceCommand">
<tag name="console.command"/>
</service>
Without this litte adaptation, your command name will not be displayed and therefore not accessible.

Resources