How to make #depends works with ZF2 AbstractHttpControllerTestCase - zend-framework2

I'm having trouble passing a variable from one test unit to another, through dependency. Here is my test controller:
<?php
namespace SyncTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use PHPUnit_Framework_ExpectationFailedException;
class TestControllerTest extends AbstractHttpControllerTestCase
{
public function testA()
{
$id = 11;
$this->assertEquals($id, 11);
return $id;
}
/*
* #depends testA
*/
public function testB($id)
{
$this->assertEquals($id, 11);
}
}
When I run phpunit, it gives the following error:
1) SyncTest\Controller\ImageControllerTest::testB
Missing argument 1 for SyncTest\Controller\ImageControllerTest::testB()
Any clue about what I am doing wrong?

/*
* #depends testA
*/
is not a valid annotation. You have to use
/**
* #depends testA
*/
otherwise PHP's Reflection API does not recognize it and in turn does not expose it to PHPUnit.

Related

TYPO3 xClass and Dependency Injection

I want to override an Extbase Controller, e.g. news in TYPO3 v10.4. Therefore I register an XClass in my ext_locaconf.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\GeorgRinger\News\Controller\NewsController::class] = [
'className' => \Clickstorm\CsEvents\Controller\EventController::class
];
Afterwards I want to inject my new Repository
/**
* eventDateRepository
*
* #var EventDateRepository
*/
protected $eventDateRepository = null;
/**
* Inject a eventDateRepository
*
* #param EventDateRepository $eventDateRepository
*/
public function injectEventDateRepository(EventDateRepository $eventDateRepository)
{
$this->eventDateRepository = $eventDateRepository;
}
But my eventDateRepository is null. Also when I use a constructor instead.
Even though I don't know the reason for such a behaviour, a sustainable workaround is to manually instantiate your dependencies in the initializeAction method.
In my case, this is what I wrote:
/**
* #var \TYPO3\CMS\Extbase\Service\ImageService
*/
protected $imageService;
public function initializeAction()
{
parent::initializeAction();
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->imageService = $objectManager->get(\TYPO3\CMS\Extbase\Service\ImageService::class);
}

PHPUnit: How do I mock an EntityManager without mocking the Metadata?

Working with PHPUnit and Doctrine I frequently end up writing very large methods for mocking Doctrines ClassMetadata, although in my opinion it does not need to be mocked, because it can be seen as stable. Still I need to mock the EntityManager because I don't want Doctrine to connect to a database.
So here's my question: How do I get my ClassMetadata via the EntityManager mock without needing a database connection? For all eventual database calls the EntityManager still needs to be a mock, I just don't want to write all my metadata down again.
I'm using the DoctrineModule for Zend 2 so it would be useful to be able to use my configuration to get the Metadata object, but I assume it's also ok to read the required sections manually.
Example:
public function testGetUniqueFields()
{
$this->prepareGetUniqueFields(); // about 50 lines of mocking ClassMetadata
$entity = 'UniqueWithoutAssociation';
$unique = $this->handler->getUniqueFields($entity);
$expected = ["uniqueColumn"];
$this->assertEquals($expected, $unique,
'getUniqueFields does not return the unique fields');
}
And the code of the actual class:
public function getUniqueFields($class)
{
$unique = array();
$metadata = $this->getClassMetadata($class);
$fields = $metadata->getFieldNames();
foreach ($fields as $field) {
if($metadata->isUniqueField($field) && !$metadata->isIdentifier($field)) {
$unique[] = $field;
}
}
return $unique;
}
The test works like expected, but every time I test another method or another behavior of the method, I need to prepare the mocks again or combine past definitions. Plus, the 50 lines I need for this code are the least I have in this test. Most of the test class is all about the ClassMetadata mock. It is a time consuming and - if you see ClassMetadata as a stable component - unnecessary work.
After spending many hours starring at the Doctrine source code, I found a solution.
Once again, this solution is only if you are working with Doctrines ClassMetadata object so often that it becomes unclean to mock every method call. In every other case you should still create a mock of ClassMetadata.
Still, since the composers minimum stability setting was set to stable, such components can be seen as stable so there is no absolute need to create a mock object.
ClassMetadata depends on several other Doctrine classes, which are all injected through the ubiquitous EntityManager:
Doctrine\ORM\Configuration to get the entity path
Doctrine\Common\Annotations\AnnotationReader and Doctrine\ORM\Mapping\Driver\AnnotationDriver injected through the Configuration object
Doctrine\DBAL\Connection to get the database platform in order to know about the identifier strategy. This object should be mocked so no database calls are possible
Doctrine\DBAL\Platforms\AbstractPlatform as mentioned
Doctrine\Common\EventManager to trigger some events
For single test methods or simple method calls I created a method returning an EntityManager mock object that is capable of returning a valid ClassMetadata object:
/**
* #return EntityManager|\PHPUnit_Framework_MockObject_MockObject
*/
public function getEmMock()
{
$dir = __DIR__."/Asset/Entity/";
$config = Setup::createAnnotationMetadataConfiguration(array($dir), true);
$eventManager = new \Doctrine\Common\EventManager();
$platform = new PostgreSqlPlatform();
$metadataFactory = new ClassMetadataFactory();
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
$connectionMock = $this->getMockBuilder('Doctrine\DBAL\Connection')
->disableOriginalConstructor()
->getMock();
$connectionMock->expects($this->any())
->method('getDatabasePlatform')
->will($this->returnValue($platform));
/** #var EntityManager|\PHPUnit_Framework_MockObject_MockObject $emMock */
$emMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$metadataFactory->setEntityManager($emMock);
$emMock->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($config));
$emMock->expects($this->any())
->method('getConnection')
->will($this->returnValue($connectionMock));
$emMock->expects($this->any())
->method('getEventManager')
->will($this->returnValue($eventManager));
$emMock->expects($this->any())
->method('getClassMetadata')
->will($this->returnCallback(function($class) use ($metadataFactory){
return $metadataFactory->getMetadataFor($class);
}));
return $emMock;
}
Here you could even manipulate all objects by calling their getters created for the EntityManager mock. But that wouldn't be exactly clean and the method remains inflexible in some cases. Still a simple solution and you could e.g. add some parameters and put the method in a trait to reuse it.
For further needs, I created an abstract class that offers a maximum of flexibility and allows you to mock everything else or create some components in a whole other way.
It needs two configurations: The entity path and the platform object. You can manipulate or replace any object by setting it in the setUp method and then get the required EntityManager mock with getEmMock().
A little bit larger, but here it is:
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Tools\Setup;
/**
* Class AbstractTestWithMetadata
* #author Marius Teller
*/
abstract class AbstractTestWithMetadata extends \PHPUnit_Framework_TestCase
{
const EXCEPTION_NO_ENTITY_PATHS_SET = "At least one entity path must be set";
const EXCEPTION_NO_PLATFORM_SET = "An instance of Doctrine\\DBAL\\Platforms\\AbstractPlatform must be set";
/**
* #var array
*/
protected $entityPaths = [];
/**
* #var AbstractPlatform
*/
protected $platform;
/**
* #var EntityManager
*/
protected $emMock;
/**
* #var Connection
*/
protected $connectionMock;
/**
* #var Configuration
*/
protected $configuration;
/**
* #var EventManager
*/
protected $eventManager;
/**
* #var ClassMetadataFactory
*/
protected $classMetadataFactory;
/**
* #return array
* #throws \Exception
*/
public function getEntityPaths()
{
if($this->entityPaths === []) {
throw new \Exception(self::EXCEPTION_NO_ENTITY_PATHS_SET);
}
return $this->entityPaths;
}
/**
* #param array $entityPaths
*/
public function setEntityPaths(array $entityPaths)
{
$this->entityPaths = $entityPaths;
}
/**
* add an entity path
* #param string $path
*/
public function addEntityPath($path)
{
$this->entityPaths[] = $path;
}
/**
* #return AbstractPlatform
* #throws \Exception
*/
public function getPlatform()
{
if(!isset($this->platform)) {
throw new \Exception(self::EXCEPTION_NO_PLATFORM_SET);
}
return $this->platform;
}
/**
* #param AbstractPlatform $platform
*/
public function setPlatform(AbstractPlatform $platform)
{
$this->platform = $platform;
}
/**
* #return EntityManager
*/
public function getEmMock()
{
if(!isset($this->emMock)) {
/** #var EntityManager|\PHPUnit_Framework_MockObject_MockObject $emMock */
$emMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$config = $this->getConfiguration();
$connectionMock = $this->getConnectionMock();
$eventManager = $this->getEventManager();
$classMetadataFactory = $this->getClassMetadataFactory();
$classMetadataFactory->setEntityManager($emMock);
$emMock->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($config));
$emMock->expects($this->any())
->method('getConnection')
->will($this->returnValue($connectionMock));
$emMock->expects($this->any())
->method('getEventManager')
->will($this->returnValue($eventManager));
$emMock->expects($this->any())
->method('getClassMetadata')
->will($this->returnCallback(function($class) use ($classMetadataFactory){
return $classMetadataFactory->getMetadataFor($class);
}));
$this->setEmMock($emMock);
}
return $this->emMock;
}
/**
* #param EntityManager $emMock
*/
public function setEmMock($emMock)
{
$this->emMock = $emMock;
}
/**
* #return Connection
*/
public function getConnectionMock()
{
if(!isset($this->connectionMock)) {
$platform = $this->getPlatform();
/** #var Connection|\PHPUnit_Framework_MockObject_MockObject $connectionMock */
$connectionMock = $this->getMockBuilder('Doctrine\DBAL\Connection')
->disableOriginalConstructor()
->getMock();
$connectionMock->expects($this->any())
->method('getDatabasePlatform')
->will($this->returnValue($platform));
$this->setConnectionMock($connectionMock);
}
return $this->connectionMock;
}
/**
* #param Connection $connectionMock
*/
public function setConnectionMock($connectionMock)
{
$this->connectionMock = $connectionMock;
}
/**
* #return Configuration
*/
public function getConfiguration()
{
if(!isset($this->configuration)) {
$config = Setup::createAnnotationMetadataConfiguration($this->getEntityPaths(), true);
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
$this->setConfiguration($config);
}
return $this->configuration;
}
/**
* #param Configuration $configuration
*/
public function setConfiguration(Configuration $configuration)
{
$this->configuration = $configuration;
}
/**
* #return EventManager
*/
public function getEventManager()
{
if(!isset($this->eventManager)) {
$this->setEventManager(new EventManager());
}
return $this->eventManager;
}
/**
* #param EventManager $eventManager
*/
public function setEventManager($eventManager)
{
$this->eventManager = $eventManager;
}
/**
* #return ClassMetadataFactory
*/
public function getClassMetadataFactory()
{
if(!isset($this->classMetadataFactory)) {
$this->setClassMetadataFactory(new ClassMetadataFactory());
}
return $this->classMetadataFactory;
}
/**
* #param ClassMetadataFactory $classMetadataFactory
*/
public function setClassMetadataFactory(ClassMetadataFactory $classMetadataFactory)
{
$this->classMetadataFactory = $classMetadataFactory;
}
}
One more hint: You might have problems with annotations of other classes, e.g. Zend\Form\Annotation\Validator. Such annotations will throw an exception in Doctrines parser because this parser does not use auto loading and only checks for already loaded classes. So if you still want to use them, you just have to include them manually before parsing the classes annotations.

Implement ModuleManagerInterface

started with a new ZF2 project an get following error
Argument 1 passed to ZendDeveloperTools\Module::init() must implement interface Zend\ModuleManager\ModuleManagerInterface, null given, called in /vendor/zendframework/zend-modulemanager/src/Listener/InitTrigger.php on line 33 and defined in /vendor/zendframework/zend-developer-tools/src/ZendDeveloperTools/Module.php on line 34
It doesn't matter which Module is first in application.config.php I always got this error.
This is mine Module.php file. The error is saying that you need to pass a ModuleManagerInterface as first parameter in your init method. That init method has an interface. Have a look at the Zend\ModuleManager\Feature\*Interface.php files and you will see your mistake.
namespace Application;
use Zend\Mvc\MvcEvent;
use Zend\EventManager\EventInterface;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\Feature\BootstrapListenerInterface;
use Zend\ModuleManager\Feature\InitProviderInterface;
use Zend\ModuleManager\ModuleManagerInterface;
class Module implements AutoloaderProviderInterface, ConfigProviderInterface, BootstrapListenerInterface, InitProviderInterface
{
/**
* Setup module layout
*
* #param $moduleManager ModuleManager
*/
public function init(ModuleManagerInterface $moduleManager)
{
$moduleManager->getEventManager()->getSharedManager()->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function (MvcEvent $e) {
$e->getTarget()->layout('layout/layout');
});
}
/**
* Listen to the bootstrap event
*
* #param EventInterface $e
*/
public function onBootstrap(EventInterface $e)
{
}
/**
* #return array|\Traversable
*/
public function getConfig()
{
return include __DIR__.'/config/module.config.php';
}
/**
* Return an array for passing to Zend\Loader\AutoloaderFactory.
*
* #return array
*/
public function getAutoloaderConfig()
{
return [
'Zend\Loader\ClassMapAutoloader' => [
__DIR__.'/autoload_classmap.php',
],
'Zend\Loader\StandardAutoloader' => [
'namespaces' => [
__NAMESPACE__ => __DIR__.'/src/'.__NAMESPACE__,
],
],
];
}
}

Porting javascript to dart

I'd like to port this javascript code to dart.
function Beagle() {
this.argv_ = null;
this.io = null;
};
Beagle.prototype.run = function() {
this.io = this.argv_.io.push();
};
runCommandClass(Beagle);
the probleme is
How to create object Beagle
How to create prototype object Beagle.prototype.run
This kind of Js code (function definition and prototype changes) can be ported to a Dart class. You can follow these main rules :
function Xxxx(){/* js code to init */} (pseudo Js class) translates to :
class Xxxx {
/// constructor
Xxxx() {
/* Dart code to init */
}
}
when you have contructor parameters like in function Xxxx(param1, param2){/* js code to init */} you have to create an other constructor with those parameters :
class Xxxx {
/// constructor with parameters
Xxxx(param1, param2) {
/* Dart code to init with param1, param2 */
}
}
Xxxx.prototype.method1 = function(p1, p2, p3){/* js code for method */} are like methods that have to be translated to :
class Xxxx {
// .... other code
/// method
method1(p1, p2, p3) {
/* Dart code */
}
}
To make your code more clear you can also add type annotations on methods and constructors. This is recommanded by the Dart Style Guide.
Type annotations are important documentation for how a library should be used. Annotating the parameter and return types of public methods and functions helps users understand what the API expects and what it provides.
For instance :
class Xxxx {
/// constructor
Xxxx(String param1, int param2) {
/* Dart code to init with param1, param2 */
}
/// method
void method1(num p1, String p2, DateTime p3) {
/* Dart code */
}
}
class Beagle { //
Map argv_;
int io;
Map portInfo;
// could make sense to make this a constructor, that depends how the Terminal class uses it (didn't look)
void run(this.argv_) {
this.portInfo_ = JSON.parse(this.argv_['argString']); // not tested
io = argv_['io'].length;
}
void sendString_(String s) { // no idea what the underlines at the end of argv_, sendString_, ... are for
// ...
}
void onRead_(String s) {}
void onTerminalResize_(int width, int height) {}
void exit(code) {
// ...
}
void close() {
// ...
}
}
var b = new Beagle(); // not translated from the JS source - just added to show how to create a new object from the Beagle class
b.run(argvFromSomewhere);
This includes a some guessing about what the intention of the JavaScript code might be.
I prefer using specific types when porting from JavaScript. It helped me a lot finding bugs and understanding the intention. When I guessed the wrong type I get an exception at runtime, then I can reason about why I got an unexpected type and which of my assumptions were wrong.

zf2 JSON-RPC server how to return custom error

I'm looking for the proper way to return a custom error from a JSON-RPC exposed class.
JSON-RPC has a special format for reporting error conditions. All errors need to provide, minimally, an error message and error code; optionally, they can provide additional data, such as a backtrace.
Error codes are derived from those recommended by the XML-RPC EPI project. Zend\Json\Server appropriately assigns the code based on the error condition. For application exceptions, the code ‘-32000’ is used.
I will use the divide method of the sample code from documentation to explain:
<?php
/**
* Calculator - sample class to expose via JSON-RPC
*/
class Calculator
{
/**
* Return sum of two variables
*
* #param int $x
* #param int $y
* #return int
*/
public function add($x, $y)
{
return $x + $y;
}
/**
* Return difference of two variables
*
* #param int $x
* #param int $y
* #return int
*/
public function subtract($x, $y)
{
return $x - $y;
}
/**
* Return product of two variables
*
* #param int $x
* #param int $y
* #return int
*/
public function multiply($x, $y)
{
return $x * $y;
}
/**
* Return the division of two variables
*
* #param int $x
* #param int $y
* #return float
*/
public function divide($x, $y)
{
if ($y == 0) {
// Say "y must not be zero" in proper JSON-RPC error format
// e.g. something like {"error":{"code":-32600,"message":"Invalid Request","data":null},"id":null}
} else {
return $x / $y;
}
}
}
$server = new Zend\Json\Server\Server();
$server->setClass('Calculator');
if ('GET' == $_SERVER['REQUEST_METHOD']) {
// Indicate the URL endpoint, and the JSON-RPC version used:
$server->setTarget('/json-rpc.php')
->setEnvelope(Zend\Json\Server\Smd::ENV_JSONRPC_2);
// Grab the SMD
$smd = $server->getServiceMap();
// Return the SMD to the client
header('Content-Type: application/json');
echo $smd;
return;
}
$server->handle();
p.s. Yes I tried Google search.
Disclaimer: I have no experience in using Zend\Json\Server whatsoever :)
If you talk about an error response, I can correlate that to the Server::fault() method (also available on Github). So I assume if fault() is called and injected into the respones, it would return the response with error messages according to your referred recommended XML-RPC server standard.
The handler method proxies the actual work to _handle() (linked to the source) where a try/catch is encapsuling the dispatching to the (in your case) Calculator class.
The fault is called based on the exception message and exception code. As such I think it's simply throwing an exception and setting the right message/code there:
use Zend\Json\Server\Error;
class Calculator
{
public function divide($x, $y)
{
if (0 === $y) {
throw new InvalidArgumentException(
'Denominator must be a non-zero numerical',
Error::ERROR_INVALID_PARAMS
);
}
// Rest here
}
// Rest here
}
PS. I also changed your error code here, as to me, it feels -32602 (invalid params) is more appropriate than -32600 (invalid request.

Resources