I know how to test php output with PHPUnit library, using expectOutputString() or expectOutputString(). Now I need to be sure that output doesn't contain given string. I can do this using output buffering and searching for string inside but probably better way is to use expectOutputString() with proper expression.
How should this expression be built?
You want to use a regex, and to do a negative match you have to use the lookahead assertion syntax. E.g. to test that the output does not contain "hello":
class OutputRegexTest extends PHPUnit_Framework_TestCase
{
private $regex='/^((?!Hello).)*$/s';
public function testExpectNoHelloAtFrontFails()
{
$this->expectOutputRegex($this->regex);
echo "Hello World!\nAnother sentence\nAnd more!";
}
public function testExpectNoHelloInMiddleFails()
{
$this->expectOutputRegex($this->regex);
echo "This is Hello World!\nAnother sentence\nAnd more!";
}
public function testExpectNoHelloAtEndFails()
{
$this->expectOutputRegex($this->regex);
echo "A final Hello";
}
public function testExpectNoHello()
{
$this->expectOutputRegex($this->regex);
echo "What a strange world!\nAnother sentence\nAnd more!";
}
}
Gives this output:
$ phpunit testOutputRegex.php
PHPUnit 3.6.12 by Sebastian Bergmann.
FFF.
Time: 0 seconds, Memory: 4.25Mb
There were 3 failures:
1) OutputRegexTest::testExpectNoHelloAtFrontFails
Failed asserting that 'Hello World!
Another sentence
And more!' matches PCRE pattern "/^((?!Hello).)*$/s".
2) OutputRegexTest::testExpectNoHelloInMiddleFails
Failed asserting that 'This is Hello World!
Another sentence
And more!' matches PCRE pattern "/^((?!Hello).)*$/s".
3) OutputRegexTest::testExpectNoHelloAtEndFails
Failed asserting that 'A final Hello' matches PCRE pattern "/^((?!Hello).)*$/s".
FAILURES!
Tests: 4, Assertions: 4, Failures: 3.
This is very simple. Test that string does not contain other string:
Edit
In 2020 for PHPUnit 9.x up there is another concise approach:
$this->assertStringNotContainsString(needle, haystack);
See PHPUnit assertion doc. Not all available assertions are documented in the docs.
The good way to find these is to dd($this) for Laravel (or var_dump($this) in pure PHP) from within your PHPUnit test class and scroll through the output's methods section. There you see the available assertions including the undocumented ones.
The Older Answer
More verbose, more flexible with different assertions not only for strings.
$string='just some string';
$this->assertThat($string, $this->logicalNot($this->stringContains('script')));
// Assertion is passed.
Based on the great example of multiple assertions in the same test from http://www.kreamer.org/phpunit-cookbook/1.0/assertions/use-multiple-assertions-in-one-test
I use it to check the form fields sanitization went OK.
Within PHPUnit test I establish a test array having <script> tag in it to be sanitized. Pass it to the sanitization method under test. Serialize the sanitization result (to avoid messing with asserting an array, plus easier on my eyes when var_dump'ing serialized result).
And then apply the stringContains method within assertThat assertion as seen above and enjoy :)
Related
As I am not sure how to describe it, I didn't found any results in google or stack.
I would like to list all available console commands (which are callable by using the bin/console) with a Controller-Action so that I can forward a list of all commands to twig.
How can I realize this ?
Interesting question. You can of course just run the console command itself and capture the list of commands. Might actually be the best way.
However, there is a service called console.command_loader which has a method called getNames which does indeed return a list of command names. It implements CommandLoaderInterface.
Originally I tried to create an alias so it could be injected into an action method:
services:
Symfony\Component\Console\CommandLoader\CommandLoaderInterface:
alias: console.command_loader
But I kept getting console.command_loader not found which was puzzling since debug:container shows it. The service was tagged with container.no_preload which might have something to do with it. Not sure.
So I went and just defined the controller service:
services:
App\Controller\CommandController:
tags:
- 'controller.service_arguments'
arguments:
- '#console.command_loader'
And somewhat to my surprise it worked.
class CommandController extends AbstractController
{
public function __construct(private CommandLoaderInterface $cl)
{
}
#[Route('/commands', name: 'app_commands')]
public function commands(): Response
{
$names = $this->cl->getNames();
dump($names);
// I happen to have a command called app:init
$initCommand = $this->cl->get('app:init');
dump($initCommand->getDescription());
//return $this->render('default/index.html.twig', [
// 'controller_name' => 'DefaultController ' . 'Commands',
//]);
}
}
This was all done in Symfony 6. Did not happen to have a Symfony 3 app handy. Your first step would be to confirm that Symfony 3 also has the service with bin/console debug:container console.command_loader. If it does not have such a service then poke around a bit and see if it has something similar.
I've been playing with server side Kotlin, Spring MVC and Jackson.
I built a simple application using http://start.spring.io/, but I might have made a mistake in the JsonView annotation.
This:
#RestController
class MyRestController {
#RequestMapping("/user")
#JsonView(User::class)
fun getUser() : User = User("Fred",50)
}
data class User(val name: String, val age: Int)
...when called with curl
ph#sleek ~ $ curl -X GET http://localhost:8080/user; echo
{}
ph#sleek ~ $
...the result is {} when I expected {"name":"Fred","age":50}. Is there something I did wrong?
Much simpler than I thought. After finding that Jackson was able to serialise the object just fine, I started making adjustments, and found the default was better than configuration:
class MyRestController {
#RequestMapping("/user")
fun getUser() : User = User("Fred",50)
}
Perfect:
ph#sleek ~ $ curl -X GET http://localhost:8080/user; echo
{"name":"Fred","age":50}
if you do want to use #JsonView (which is necessary in many scenarios), I was running into the same empty object problem until I added the jackson-module-kotlin dependency to my project.
compile 'com.fasterxml.jackson.module:jackson-module-kotlin'
See here: https://stackoverflow.com/a/48019143/5258628
Here is the issue. Let assume I have two mutable modules:
class DbModule extends Module { bind[JdbcBackend#Database] toProvider
inject[JdbcDriver].backend.Database.forURL(
inject[String]("db.url"),
inject[String]("db.username"),
inject[String]("db.password"), null,
inject[String]("db.driver")
) }
and here is the corresponding config:
resources/application.conf:
db { url="postgres url" username="db_user" password="db_password" driver="cc" }
Somewhere in the code I do:
implicit val inj = TypesafeConfigInjector() :: new AppModule
However this injector gives the following exception:
caldi.InjectException: No binding found with following identifiers:
* TypeTagIdentifier(String) * StringIdentifier(db.url)
The order in Scaldi is important: the binding is resolved from left to right.
The :: operator, as stated in the docs, composes two injectors by inverting the operands. Thus, in your case, AppModule is resolved first, hence it cannot find the config params injected.
To solve your problem, use the ++ operator to keep your injectors in order.
I hope this is helpful.
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
I was learning about unit testing and I attempted to resolve the following issue:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication
... using the only answer given at:
Simple ZF2 Unit Tests for a controller using ZfcUser
So my setUp function looks the same. Unfortunately, I get the error message:
Zend\Mvc\Exception\InvalidPluginException: Plugin of type Mock_ZfcUserAuthentication_868bf824 is invalid; must implement Zend\Mvc\Controller\Plugin\PluginInterface
It is caused at this part of the code (split up in my code in the same way):
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock); // Error refers to this line.
The $authMock object is apparently not implementing plugininterface, which I need to implement to pass into setService.
Is $authMock not meant to be passed there for it's use in unit testing? Should I be using a different (unit-testing oriented) setService method?
I need a way to handle logging into my application, or my unit testing is pointless.
Thanks for any advice.
=== Edit (11/02/2013) ===
I wanted to focus on this part for clarification, as I think this is the problem area:
// Getting mock of authentication object, which is used as a plugin.
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication');
// Some expectations of the authentication service.
$authMock -> expects($this->any())
-> method('hasIdentity')
-> will($this->returnValue(true));
$authMock -> expects($this->any())
-> method('getIdentity')
-> will($this->returnValue($ZfcUserMock));
// At this point, PluginManager disallows mock being assigned as plugin because
// it will not implement plugin interface, as mentioned.
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
If the mock doesn't handle necessary implementations, how else am I to pretend to login?
You have a problem with name-spacing or your autoloader.
When you are creating your mock, the class definition of ZfcUser\Controller\Plugin\ZfcUserAuthentication is not being found. So PHPUnit creates a mock that only extends this class for your test. If the class was available then PHPUnit will use the actual class to extend when making its mock, which will then use the parent classes/interfaces.
You can see this logic here: https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php
if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
!interface_exists($mockClassName['fullClassName'], $callAutoload)) {
$prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
if (!empty($mockClassName['namespaceName'])) {
$prologue = 'namespace ' . $mockClassName['namespaceName'] .
" {\n\n" . $prologue . "}\n\n" .
"namespace {\n\n";
$epilogue = "\n\n}";
}
$cloneTemplate = new Text_Template(
$templateDir . 'mocked_clone.tpl'
);
So if there is no class or interface, PHPUnit will actually create one itself so that the mock will meet the type hinting of original class name. However, any parent classes or interfaces will not be included because PHPUnit is not aware of them.
This would be due to not including the proper namespace in your test or having a problem in your autoloader. It is difficult to tell without actually seeing the entire test file.
Alternatively rather than mocking ZfcUser\Controller\Plugin\ZfcUserAuthentication, you could mock the Zend\Mvc\Controller\Plugin\PluginInterface in your test and pass that into the plugin manager. Though if you are type-hinting for the plugin in your code, your test still won't work.
//Mock the plugin interface for checking authorization
$authMock = $this->getMock('Zend\Mvc\Controller\Plugin\PluginInterface');
// Some expectations of the authentication service.
$authMock -> expects($this->any())
-> method('hasIdentity')
-> will($this->returnValue(true));
$authMock -> expects($this->any())
-> method('getIdentity')
-> will($this->returnValue($ZfcUserMock));
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
I just made an example for the FlashMessenger plugin. You should just use the ControllerPluginManager to override the ControllerPlugin. Make sure that your application bootstrap calls setApplicationConfig();
<?php
namespace SimpleTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class SimpleControllerTest extends AbstractHttpControllerTestCase {
public function testControllerWillAddErrorMessageToFlashMessenger()
{
$flashMessengerMock = $this->getMockBuilder('\Zend\Mvc\Controller\Plugin\FlashMessenger', array('addErrorMessage'))->getMock();
$flashMessengerMock->expects($this->once())
->method('addErrorMessage')
->will($this->returnValue(array()));
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->get('ControllerPluginManager')->setService('flashMessenger', $flashMessengerMock);
$this->dispatch('/error/message');
}
}?>