How to use PHPUnit assert methods in a Codeception functional test? - phpunit

I'm using Codeception for unit, functional, and acceptance tests of my Laravel 4 PHP application.
My unit tests look this:
use Codeception\Util\Stub;
class ExampleTest extends \Codeception\TestCase\Test
{
public function testExample()
{
$example = true;
$this->assertSame($example, true);
}
}
My functional tests look like this:
use \TestGuy;
class ExampleCest
{
public function example(TestGuy $I)
{
$I->amOnPage('/auth/login');
$I->see('Sign in');
}
}
But I also want to use PHPUnit assert methods in my functional tests. But when I try to, I get this error:
Call to undefined method ExampleCest::assertSame()
How do I use PHP assert methods in a Codeception functional test?

Since Codeception 2.1 (not 2.0) you can use it like the other asserts with:
$I->assertSame($expected, $actual, $message);
But don't forget to enable the Asserts module in your config - e.g.:
class_name: UnitTester
modules:
enabled: [ Asserts ]
Please note: You might need to change your configuration when upgrading to 2.1 - see upgrade instructions: http://codeception.com/06-19-2015/codeception-2.1-rc.html

\PHPUnit_Framework_Assert::assertSame()

In Codeception 4 just add the Asserts Module:
modules:
enabled:
- \Codeception\Module\Asserts
to your suite.yml config file and run codeception build

Another workaround can be to use Helper Methods in test suite.
For example for assertSame() method
class ExpectedHelper extends \Codeception\Module
{
protected $test;
function _before(\Codeception\TestCase $test) {
$this->test = $test;
}
function assertSame($expected, $actual, $message = '')
{
$this->test->assertSame($exception, $actual, $message);
}
}
where ExpectedHelper being the test suite Helper name (eg: UnitHelper, FunctionalHelper) which should be under _support folder
and you can use it in your test as $I->assertSame('12340','12340');

Related

Install Symfony without symfony/runtime and with old index.php

I am trying to add Symfony 5.4 to my legacy project. There is a pretty nice documentation on how to do this, but there's a big problem - the documentation assumes "normal" Symfony, but each time I try to install Symfony using their recommended way of composer create-project, I get a Symfony version with symfony/runtime - the big problem here, is that this version has a completely different index.php:
<?php
use App\Kernel;
require_once dirname(_DIR_).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
The documentation found here is based on a completely different index file.
I did find that I can remove the runtime package, and just copy old index, and it works for the most part, but then you also have problems with console.php and I worry that if I go this route there will be more and more problems caused by my installation expecting symfony/runtime and me manually removing it's
I tried installing Symfony 5.3 as well as different patches of 5.4, all came with this installed, even though I did work on some 5.3 / 5.4 projects and had the old school index.php file.
Does anyone know how to currently install Symfony with the "old" index.php, console.php etc.?
Thanks!
So the task is to migrate from a non-Symfony legacy app to a Symfony app. The basic idea is to allow the Symfony app to process a request and then hand it off to the legacy app if necessary. The Symfony docs show how to do this but but relies on the older style index.php file. The newer runtime based approach is a bit different.
But in the end all it really takes is a couple of fairly simple classes. A runner class takes care of creating a request object and turning it into a response. This is where you can add the bridge to your legacy app. It's a clone of Symfony's HttpKernelRunner class:
namespace App\Legacy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\Runtime\RunnerInterface;
class LegacyRunner implements RunnerInterface
{
private $kernel;
private $request;
public function __construct(HttpKernelInterface $kernel, Request $request)
{
$this->kernel = $kernel;
$this->request = $request;
}
public function run(): int
{
$response = $this->kernel->handle($this->request);
// check the response to see if it should be handed off to legacy app
dd('Response Code ' . $response->getStatusCode());
$response->send();
if ($this->kernel instanceof TerminableInterface) {
$this->kernel->terminate($this->request, $response);
}
return 0;
}
}
Next you need to wire up runner by extending the SymfonyRuntime::getRunner method:
namespace App\Legacy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Runtime\RunnerInterface;
use Symfony\Component\Runtime\SymfonyRuntime;
class LegacyRuntime extends SymfonyRuntime
{
public function getRunner(?object $application): RunnerInterface
{
if ($application instanceof HttpKernelInterface) {
return new LegacyRunner($application, Request::createFromGlobals());
}
return parent::getRunner($application);
}
}
Finally, update composer.json to use your legacy runtime class:
"extra": {
...
"runtime": {
"class": "App\\Legacy\\LegacyRuntime"
}
}
After updating composer.json do a composer update for the changes to take effect and start your server. Navigate to a route and you should hit the dd statement.

How can I use the "dump()" twig function in a template that is going to be rendered on production?

I would like to implement a debug output in our test environments in which I would like to output service requests that the application sends.
For this I wanted to use the symfony/twig function dump(), because here the output is wonderfully formatted for all types of variables and also offers the option of opening and closing the structure.
Pseudo-code would be something like this
{% if debugEnabled %}
{{dump (debugInfos)}}
{% endif %}
Unfortunately, "dump" is part of the Symfony DebugBundle, which for good reasons is not loaded in Prod environments and which should stay that way:
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
In the Symfony documentation says:
By design, the dump() function is only available in the dev and test
environments, to avoid leaking sensitive information in production. In
fact, trying to use the dump() function in the prod environment will
result in a PHP error.
I don't want to use dump() in production environments at all, but only locally to output our service requests.
However, I cannot implement a code like above because an error always occurs in production (undefined function dump()) of course, since dump() is not loaded at all.
You can create your own function which tests if the extension is loaded.
As you can see in the source the function twig_var_dump is registered, so you can test if the function exists or not to determine if the debug extension was loaded or not.
Should be fine to register your own function as dump as long as your extension gets loaded after the debug extension
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\Environment;
class AppExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('dump', [$this, 'dump'] , ['needs_context' => true, 'needs_environment' => true, ]),
];
}
public function dump(Environment $env, $context, ...$vars)
{
if (!function_exists('\twig_var_dump')) return;
return \twig_var_dump($env, $context, ...$vars);
}
}
sidenote: Not tested this but should work
Create your own extension that's only loaded in production.
Create a folder (for example Twig) in your src directory.
Make that folder excluded from autowiring if you use the default:
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/Tests/'
- '../src/Twig/' # This is the directory excluded from autowiring
Then create a config/services_prod.yaml file with this content:
services:
App\Twig\FakeDebugExtension: # replace with your class name
tags:
- twig.extension # not needed if you use autoconfiguration
Then create a class with this content:
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
final class FakeDebugExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('dump', [$this, 'fakeDump']),
];
}
public function fakeDump(...$arguments): void
{
}
}

Silverstripe 4 SapphireTest class can't be found

I've upgraded from SilverStripe 3 to 4 and now my phpUnit tests own't run because they can't find any of my Custom Classes.
There must be something missing from an autoloader or something.
I have a simple test like this
use SilverStripe\Dev\SapphireTest;
class EntityTest extends SapphireTest
{
var $Entity;
function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
parent::setUp(); // TODO: Change the autogenerated stub
$this->Entity = new \My\API\Client\Model\Entity();
}
function testMethods(){
$this->assertMethodExist($this->Entity,'setName');
}
function assertMethodExist($class, $method) {
$oReflectionClass = new ReflectionClass($class);
assertThat("method exist", true, $oReflectionClass->hasMethod($method));
}
}
and when running I get:
$ php vendor/phpunit/phpunit/phpunit mysite/tests/EntityTest.php
Fatal error: Class 'SilverStripe\Dev\SapphireTest' not found
I ran into a similar issue with SilverStripe 4.1, here is what I found (and resolved).
1) As of 4.1, you need to use --prefer-source instead of --prefer-dist to get the test code. Test code is now omitted from the distributed packages, see https://github.com/silverstripe/silverstripe-framework/issues/7845
2) phpunit must be in require-dev at version ^ 5.7 - I had a different value and this was the cause of the autoload issue.
I've created a test module for reference, see https://github.com/gordonbanderson/travistestmodule
Cheers
Gordon
You're probably missing the test bootstrapping. SS4 still relies on the SilverStripe class manifest to register available classes (not just PSR-4 autoloaders), so you need to include it. Try either of these:
$ vendor/bin/phpunit --bootstrap vendor/silverstripe/framework/tests/bootstrap.php mysite/tests
or create a phpunit.xml file in your root project:
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
</phpunit>
You may also use the equivalent file from the CMS module instead, but you probably won't see any differences until you start to integrate your testsuite into a CI provider.

Override a symfony service tag with a compiler pass

I'm trying to override a tag in a symfony service definition with a compiler pass. The service as an example would be data_collector.translation.
The goal is to deactivate the data collector service to disable the element in the symfony web developer toolbar. To do this, I have to set the priority of the data_collector tag to 0.
I could also override it in my own service definition:
services:
data_collector.translation:
class: 'Symfony\Component\Translation\DataCollector\TranslationDataCollector'
tags:
- {name: 'data_collector', priority: '0'}
arguments: [#translator.data_collector]
But as I want to do this for a few of the data collectors, I would need to know the mandatory arguments for the data collector definition. The priority works the same for all collectors and therefore I would only need the name of the collector to disable it.
So I wrote the following compiler pass:
class DataCollectorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('data_collector.translation')) {
return;
}
$definition = $container->getDefinition('data_collector.translation');
$tags = $definition->getTags();
$tags['data_collector'][0]['priority'] = 0;
$definition->setTags($tags);
$container->setDefinition('data_collector.translation', $definition);
}
}
To make things more wired: When I run this command:
$ php app/console container:debug --show-private --tag='data_collector'
I get the following output:
data_collector.translation #WebProfiler/Collector/translation.html.twig translation 0 Symfony\Component\Translation\DataCollector\TranslationDataCollector
So the priority even in the debugger is set to 0.
But for which reason ever the element is still shown in the toolbar.
What did I do wrong here? Is there another mechanism for overwriting a tag within a compiler pass?
The compiler pass does run (tested it with printing out stuff)
I'm using Symfony 2.7.1
Turns out the code does work, the only problem is, that the CompilerPass is run after the ProfilerPass which is part of the FrameworkBundle. Putting my bundle with the CompilerPass before the FrameworkBundle in the AppKernel solves the problem (more information here). For not even initiating the data collectors it's better to remove all tags instead of just setting the priority to 0.
That's what the final solution looks like:
class DataCollectorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$collectorsToRemove = [
'data_collector.form',
'data_collector.translation',
'data_collector.logger',
'data_collector.ajax',
'data_collector.twig'
];
foreach($collectorsToRemove as $dataCollector) {
if (!$container->hasDefinition($dataCollector)) {
continue;
}
$definition = $container->getDefinition($dataCollector);
$definition->clearTags();
}
}
}
Can you try this?
if (!$container->hasDefinition('data_collector.form')) {
return;
}
$definition = $container->getDefinition('data_collector.form');
$definition->clearTags();
$container->setDefinition('data_collector.form', $definition);
Why not use your compiler pass to manipulate directly the service Definition of the service holding all these collectors ?
If I look at the compiler pass responsible for loading the data collector, it seems that they are all injected using a method call injection.
You could use your compiler pass to rewrite the method call array using methods like setMethodCalls, removeMethodCall, ... of the Definition entity.
The method call manipulation documentation : link

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