ZF2 - Mocking service requested in Module.php - php

I am trying to test a controller of my ZF2 application. Suppose this controller is in my A module.
In the onBootstrap method of the Module.php of the module A I am using the service manager to retrieve a service of another module, say B, that I am not loading.
How can set a mock of the requested service in the service manager? Mind that I can not use $this->getApplicationServiceLocator() to do this in my test, since this is already calling the Module.onBootstrap method of my A module.
To post some code, this is what I am doing at the moment
bootstrap.php
namespace Application;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$modulePath = static::findParentPath('module');
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
throw new RuntimeException('Cannot locate autoload.php');
}
$config = [
'modules' => [
'Application',
],
'module_listener_options' => [
'module_paths' => [
$modulePath,
$vendorPath
]
]
];
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
}
Bootstrap::init();
my actual test class
namespace Application\Functional;
use Application\Bootstrap;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class ValidateCustomerRegistrationTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$applicationConfig = $serviceManager->get('ApplicationConfig');
$this->setApplicationConfig($applicationConfig);
parent::setUp();
}
public function testRegisterValidUserWithOnlyEquomobiliData()
{
$this->getApplicationServiceLocator();
}
}
Module.php simplified
namespace Application
Class Module
{
public function onBootstrap(MvcEvent $e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$service = $serviceManager->get('Service\From\Other\Module');
}
}

There is not enough data here to help you directly. It would be more useful to have the function ValidateCustomerRegistrationTest->getApplicationServiceLocator() for starters.
I hope I can help you indirectly.
Refactoring could be helpful
When I am writing a unit test I start with a few personal rules.
Only test the code being testing. Mock EVERYTHING else. No need to test something that should have it's own tests already.
How a function works should not be important. Only the input/output. This keeps your test viable even when the core of a function changes drastically.
/**
* #param MvcEventInterface $event
* #param Service\From\Other\ModuleInterface $service
*
* #return boolean
*/
public function onBootstrap(MvcEventInterface $event, Service\From\Other\ModuleInterface $service)
{
return true;
}
Then in the test class:
public function testOnBootstrap(){
$eventMock = $this->getMock(MvcEventInterface::class);
$serviceMock = $this->getMock(ModuleInterface::class);
$module = new Module();
$result = $module->onBootstrap($eventMock, $serviceMock);
$this->assertTrue($result);
}
* I clearly do not know what you are trying to test
When refactoring is not an option
There are two mock types that can help out, that occur to me right off the bat, mock, and mockBuilder. Take a look at the PHPUnit documentation for Test Doubles.
$serviceMock = $this->getMockBuilder(ServiceManager::class)
->disableOriginalConstructor()
->setMethods([
'__construct',
'get'
])
->getMock();
$serviceMock
->expects($this->any())
->method('get')
->will($this->returnValue(
$this->getMock(ModuleManagerInterface::class)
));
Good luck, I hope you can let us know if we can help you more specifically. I would also recommend looking into SOLID Principles of Object Oriented Programing. One of many programming principles that should make your code clean, easy to extend, and easy to test.

One way to accomplish what you're trying to do may be to force an overwrite of the service in the Service Manager before dispatching the request to your test controller. Here's an example of how to do that (NOTE: since what you're doing is during the module's bootstrap process, the example may not translate 100% to your situation).
Like other people mentioned: you may still want to double-check whether you can refactor to a cleaner approach, but that's another story.

In order to mock the service manager and the calls made with it you can use mockery https://github.com/mockery/mockery. This library is framework agnostic so even if you use PHPUnit or an other tool it should work.
The bad way to use it but it should solve your problem fast is to overload the class using mockery('overload:myclass') where myclass is the instance of the service manager.
The best way to use the library is to use the depedency injection pattern to inject a mock of the service locator when your app is boostraping for the tests.
An easy way to go with it is to create a factory for your controller, inject the service locator inside. Then in your test you init the controller with the mock of the service locator as a parameter.

Related

PHP DI pattern for function driven application

I have some classes that require dependencies injected into their constructors. This allows me to inject mocks (e.g. from prophecy) for testing.
I'm interested in using a container to help configure and access these objects, and I've looked at Pimple for this (I also looked at PHP-DI although I couldn't get that to resolve stuff on a quick attempt).
All good so far. BUT, the problem I have is that the application (Drupal 7) is built around thousands of functions which do not belong to an object that can have dependencies injected into.
So I need these functions to be able to access the services from the container. Further more, for testing purposes, I need to replace the services with mocks and new mocks.
So the pattern is like:
<?php
/**
* Some controller class that uses an injected mailing service.
*/
class Supporter
{
protected $mailer;
public function __construct(MailingServiceInterface $mailer) {
$this->mailer = $mailer;
}
public function signUpForMalings($supporter_id) {
$email = $this->getSupporterEmail($supporter_id);
$this->mailer->signup($email);
}
}
Then peppered in various functions I'd use:
<?php
/**
* A form submit handler called by the platform app,
* with a signature I can't touch.
*/
function my_form_submit($values) {
global $container;
if ($values['subscribe']) {
$supporter = $container->get('supporter');
$supporter->signUpForMailings($values['supporter_id']);
}
}
Elsewhere I may need to access the mailer directly...
<?php
/**
* example function requires mailer service.
*/
function is_signed_up($email) {
global $container;
return $container->get('mailer')->isSignedUp($email);
}
And elsewhere a function that calls those functions...
<?php
/**
* example function that uses both the above functions
*/
function sign_em_up($email, $supporter_id) {
if (!is_signed_up($email)) {
my_form_submit(['supporter_id'=>$supporter_id);
return TRUE;
}
}
Let's acknowledge that these functions are a mess - that's a deliberate representation of the problem. But let's say I want to test the sign_em_up function:
<?php
public testSignUpNewPerson() {
$mock_mailer = createAMockMailer()
->thatWill()
->return(FALSE)
->whenFunctionCalled('isSignedUp', 'wilma#example.com');
// Somehow install the mock malier in the container.
$result = sign_em_up('wilma#example.com', 123);
$this->assertTrue($result);
}
// ... imagine other tests which also need to inject mocks.
While I recognise that this is using the container as a Service Locator in the various global functions, I think this is unavoidable given the nature of the platform. If there's a cleaner way, please let me know.
However my main question is:
There's a problem with injecting mocks, because the mocks need to change for various tests. Lets say I swap out the mailer service (in Pimple: $container->offsetUnset('mailer'); $container['mailer'] = $mock_mailer;), but if Pimple had already instantiated the supporter service, then that service will have the old, unmocked mailer object. Is this a limitation of the containter software, or the general container pattern, or am I Doing It Wrong, or is it just a mess because of the old-school function-centred application?
Here's what I've gone for, in absence of any other suggestions!
Container uses Pimple\Psr11\ServiceLocator
I'm using Pimple, so the container's factories may look like this
<?php
use Pimple\Container;
use Pimple\Psr11\ServiceLocator;
$container = new Container();
$container['mailer'] = function ($c) { return new SomeMailer(); }
$container['supporters'] = function ($c) {
// Create a service locator for the 'Supporters' class.
$services = new ServiceLocator($c, ['mailer']);
return new Supporter($services);
}
Then the Supporter class now instead of storing references to the objects extracted from the container when it was created, now fetches them from the ServiceLocator:
<?php
use \Pimple\Psr11\ServiceLocator;
/**
* Some controller class that uses an injected mailing service.
*/
class Supporter
{
protected $services;
public function __construct(ServiceLocator $services) {
$this->services = $services;
}
// This is a convenience function.
public function __get($prop) {
if ($prop == 'mailer') {
return $this->services->get('mailer');
}
throw new \InvalidArgumentException("Unknown property '$prop'");
}
public function signUpForMalings($supporter_id) {
$email = $this->getSupporterEmail($supporter_id);
$this->mailer->signup($email);
}
}
In the various CMS functions I just use global $container; $mailer = $container['mailer'];, but it means that that in tests I can now mock any service and know that all code that needs that service will now have my mocked service. e.g.
<?php
class SomeTest extends \PHPUnit\Framework\TestCase
{
function testSupporterGetsMailed() {
global $container;
$supporter = $container['supporter'];
// e.g. mock the mailer component
$container->offsetUnset('mailer');
$container['mailer'] = $this->getMockedMailer();
// Do something with supporter.
$supporter->doSomething();
// ...
}
}

ZF2 phpunit Zend Logger

I am trying to write unit test for my application. which as logging the information functionality.
To start with i have service called LogInfo, this how my class look like
use Zend\Log\Logger;
class LogInfo {
$logger = new Logger;
return $logger;
}
I have another class which will process data. which is below.
class Processor
{
public $log;
public function processData($file)
{
$this->log = $this->getLoggerObj('data');
$this->log->info("Received File");
}
public function getLoggerObj($logType)
{
return $this->getServiceLocator()->get('Processor\Service\LogInfo')->logger($logType);
}
}
here i am calling service Loginfo and using it and writing information in a file.
now i need to write phpunit for class Processor
below is my unit test cases
class ProcessorTest{
public function setUp() {
$mockLog = $this->getMockBuilder('FileProcessor\Service\LogInfo', array('logger'))->disableOriginalConstructor()->getMock();
$mockLogger = $this->getMockBuilder('Zend\Log\Logger', array('info'))->disableOriginalConstructor()->getMock();
$serviceManager = new ServiceManager();
$serviceManager->setService('FileProcessor\Service\LogInfo', $mockLog);
$serviceManager->setService('Zend\Log\Logger', $mockLogger);
$this->fileProcessor = new Processor();
$this->fileProcessor->setServiceLocator($serviceManager);
}
public function testProcess() {
$data = 'I have data here';
$this->fileProcessor->processData($data);
}
}
I try to run it, i am getting an error "......PHP Fatal error: Call to a member function info() on a non-object in"
i am not sure , how can i mock Zend logger and pass it to class.
Lets check out some of your code first, starting with the actual test class ProcessorTest. This class constructs a new ServiceManager(). This means you are going to have to do this in every test class, which is not efficient (DRY). I would suggest constructing the ServiceMananger like the Zend Framework 2 documentation describes in the headline Bootstrapping your tests. The following code is the method we are interested in.
public static function getServiceManager()
{
return static::$serviceManager;
}
Using this approach makes it possible to obtain the instance of ServiceManager through Bootstrap::getServiceManager(). Lets refactor the test class using this method.
class ProcessorTest
{
protected $serviceManager;
protected $fileProcessor;
public function setUp()
{
$this->serviceManager = Bootstrap::getServiceManager();
$this->serviceManager->setAllowOverride(true);
$fileProcessor = new Processor();
$fileProcessor->setServiceLocator($this->serviceManager);
$this->fileProcessor = $fileProcessor;
}
public function testProcess()
{
$mockLog = $this->getMockBuilder('FileProcessor\Service\LogInfo', array('logger'))
->disableOriginalConstructor()
->getMock();
$mockLogger = $this->getMockBuilder('Zend\Log\Logger', array('info'))
->disableOriginalConstructor()
->getMock();
$serviceManager->setService('FileProcessor\Service\LogInfo', $mockLog);
$serviceManager->setService('Zend\Log\Logger', $mockLogger);
$data = 'I have data here';
$this->fileProcessor->processData($data);
}
}
This method also makes it possible to change expectations on the mock objects per test function. The Processor instance is constructed in ProcessorTest::setUp() which should be possible in this case.
Any way this does not solve your problem yet. I can see Processor::getLoggerObj() asks the ServiceManager for the service 'Processor\Service\LogInfo' but your test class does not set this instance anywhere. Make sure you set this service in your test class like the following example.
$this->serviceManager->setService('Processor\Service\LogInfo', $processor);

PHP MVC with OOP

I started to read symfony book and try to implement on my own a really basic mvc framework for learning purpose. I use some components of symfony.
By now i have an index.php file like this
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
$frontController = new FrontController($uri);
Then i have a frontController.php file that have a basic routing
<?php
class FrontController
{
private $uri;
public function __construct($uri)
{
$this->uri = $uri;
$this->setRoute();
}
private function setRoute()
{
$request = explode("/", trim($_SERVER['REQUEST_URI']));
$controller = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$action = !empty($request[2])
? $request[2] . "Action"
: "indexAction";
$parameters = !empty($request[3])
? intval($request[3])
: null;
$response = call_user_func(array($controller, $action), $parameters);
$this->sendResponse($response);
}
private function sendResponse($response)
{
$response->send();
}
}
Then i have a series of controller (i.e. ordersController.php)
<?php
use Symfony\Component\HttpFoundation\Response;
class OrdersController
{
public function ordersAction()
{
//take data from model
$order = new OrdersModel;
$orders = $order->get_all_orders();
$html = self::render_template('templates/list.php', array('orders' => $orders));
return new Response($html);
}
public function render_template($path, array $args = null)
{
if(!empty($args))
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}
}
Now, although i know that what i have done can be improved , i would like to inject the right model when i instantiate a controller class in the FrontController class without instantiate the model instance in the method where i use that.
How can i achieve this?
And if you have some suggests to give me, please do that because i want to improve my skills.
What are you looking for is a Dependency Injection Container (DIC). It's not only about injecting "models" (actually I hate this word because of ORMs confusion), but rather about injecting any kind of services. DIC incapsulates a service instantiation logic. Then, you can use the DIC inside your FrontController to get required services and pass it to the controller. Also, the DIC introduces some kind of indirection. You don't have to know about actual service implementations, but only about its names.
The simplest DIC implementation for PHP is Pimple. It's very small project, I strictly recommend to look at its source code. Mentioned in the comments Silex uses Pimple under the hood.
To conclude, you have to do only two things:
Register a service in the DIC.
Inject this service (or a few services) to the controller.
Basic example of registering a service is:
$container['orders'] = function($c) {
return new OrdersModel();
};
Usually, FrontController has an access to such $container object as a member or FrontController can be a container by itself. Pretty often such object is called Application. Registering of all existing services have to be done before a dispatching code. Since registering a service doesn't require service instantiation (registering a service is just creation of a closure) you can freely register hundreds of such services without an extra load.
Now, you need to somehow specify controller's dependencies. One way to do it - using static field in the controller class.
class OrdersController {
public static $dependencies = ['orders', 'otherService'];
private $ordersModel;
private $otherService;
public function __construct($ordersModel, $otherService) {
$this->ordersModel = $ordersModel;
$this->otherService = $otherService;
}
}
And then you have to read list of dependencies of each controller before instantiation in the FrontController:
...
$ctrlClass = !empty($request[1])
? ucfirst($request[1]) . "Controller"
: "IndexController";
$services = [];
foreach ($ctrlClass::dependencies as $serviceName) {
$services[] = $this->container[$serviceName];
}
// Here could be better way - PHP is not my native lang.
$reflection = new ReflectionClass($classname);
$controller = $reflection->newInstanceArgs($services);
...
That is, by the end you'll get a controller instance with all required dependencies inside. Such code very easy to test.
UPD: Also, a service A can require a service B as internal dependency. Such dependency easy to satisfy in the Pimple approach:
$container['serviceA'] = function($c) {
return new ServiceA($c['serviceB']);
};
$container['serviceB'] = function($c) {
return new ServiceB();
};
In such case if your controller requires ServiceA it indirectly depends on ServiceB too. However, you don't have to resolve such indirect dependency manually.
UPD2: Further reading:
Dependency Injection with Pimple
Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler

Zend Framework 2: How to include Doctrine2 support in unit testing with phpunit?

After further research, I'm rephrasing my question: How do I mock Doctrine2's entity manager for unit testing, particularly in controllers? Essentially, I need to go through my unit tests and replace anything related to mocking a TableGateway with Doctrine2's entity manager.
(Original Post)
Some time ago I went through the official ZF2 getting started tutorial for version 2.1 and completed it, then went through the additional tutorial concerning unit testing.
http://framework.zend.com/manual/2.1/en/user-guide/overview.html
http://framework.zend.com/manual/2.1/en/tutorials/unittesting.html
Following the completion of those tutorials, I concluded that Zend_Db was not likely to handle my application needs elegantly and so started looking into integrating and using Doctrine 2 into Zend Framework 2. I struggled to find good examples and then found the following tutorial which was a good start.
http://www.jasongrimes.org/2012/01/using-doctrine-2-in-zend-framework-2/
It was here that I got hung up as I wanted to include the Doctrine 2 changes into my unit testing but couldn't figure out how. After many days of fruitless research, I put this project away for sometime and am now coming back to it. I found another decent tutorial about this subject but it concerns ZF1 so the structure is significantly different.
http://www.zendcasts.com/unit-testing-doctrine-2-entities/2011/02/
However, with fresh eyes and comparing my sample ZF2 files with the files associated with the ZF1 framework, I see at the very least that I probably need to set up the Doctrine entity manager in the phpunit Bootstrap file and then call it in the Controller tests correctly.
Currently, when I run phpunit, I get the following error:
There was 1 failure:
1) AlbumTest\Controller\AlbumControllerTest::testEditActionRedirectsAfterValidPost
Expectation failed for method name is equal to <string:find> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
In the relevant part of this test method, I currently have:
//$albumTableMock = $this->getMockBuilder('Album\Models\AlbumTable') // Zend_Db
$albumTableMock = $this->getMockBuilder('Album\Entity\Album')
->disableOriginalConstructor()
->getMock();
(I'm really not sure if 'Album\Entity\Album' is correct here but my understanding is that because of Doctrine 2, I'm no longer using 'Album\Models\Album' or 'Album\Models\AlbumTable' that I created in the initial tutorial.)
In the AlbumController, the relevant portion of code is here:
// Get the Album with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try {
//$album = $this-> getAlbumTable()->getAlbum($id); // Zend_DB
$album = $this->getEntityManager()->find('Album\Entity\Album', $id); // Doctrine
}
So my main question here would be how would I set up my phpunit Bootstrap file to include Doctrine2?
(Addendum)
I'll circle back around to the above scenario now but currently I'm trying to successfully mock the entity manager for my tests and I've commented out the above getMocks in my controller test to get down to basics. I've gotten a basic test set up for Album\Entity\Album so I think my bootstrapping is ok but I'm failing now with tests trying to access the database from the controller when using $this->dispatch(), which I don't want.
PHPUnit Failure:
1) AlbumTest\Controller\AlbumControllerTest::testIndexActionCanBeAccessed
PDOException: SQLSTATE[HY000] [1045] Access denied for user 'username'#'localhost' (using password: YES)
I don't have this user explicitly defined anywhere so I'm guessing that a config file isn't being found or loaded but I want to mock the database connection anyway.
Here's my test bootstrap:
<?php
namespace AlbumTest;
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
//use DoctrineORMModuleTest\Framework\TestCase;
//use Doctrine\Tests\Mocks;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
// Set timezone or PHPUnit reports error
date_default_timezone_set('America/Chicago');
/**
* Test bootstrap, for setting up autoloading
*/
class Bootstrap
{
protected static $serviceManager;
protected static $em; // Doctrine
public static function init()
{
$zf2ModulePaths = array(dirname(dirname(__DIR__)));
if (($path = static::findParentPath('vendor'))) {
$zf2ModulePaths[] = $path;
}
if (($path = static::findParentPath('module')) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$config = array(
'module_listener_options' => array(
'module_paths' => $zf2ModulePaths,
),
'modules' => array(
'DoctrineModule',
'DoctrineORMModule',
'Album'
)
);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
static::$em = self::getEntityManager($serviceManager); // Doctrine
}
public static function getServiceManager()
{
return static::$serviceManager;
}
public static function getEntityManager($serviceManager)
{
return $serviceManager->get('Doctrine\ORM\EntityManager');
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
$zf2Path = getenv('ZF2_PATH');
if (!$zf2Path) {
if (defined('ZF2_PATH')) {
$zf2Path = ZF2_PATH;
} else {
if (is_dir($vendorPath . '/ZF2/library')) {
$zf2Path = $vendorPath . '/ZF2/library';
}
}
}
if (!$zf2Path) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
// This function traverses up from the working directory
// until it finds the parent of the named path
// (used to find 'vendor' parent in initAutoloader).
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) return false;
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
Here's my Entity test (to hopefully prove Doctrine is bootstrapped correctly):
<?php
namespace AlbumTest\Entity;
use Album\Entity\Album;
use PHPUnit_Framework_TestCase;
class AlbumTest extends PHPUnit_Framework_TestCase
{
public function testAlbumInitialState() {
$album = new Album();
$this->assertNull($album->artist, '"artist" should initially be null');
$this->assertNull($album->id, '"id" should initially be null');
$this->assertNull($album->title, '"title" should initially be null');
}
}
And here's the relevant portions of my Controller Test:
<?php
namespace AlbumTest\Controller;
//use Album\Models\Album;
use Album\Entity\Album;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Db\ResultSet\ResultSet;
//use PHPUnit_Framework_TestCase;
class AlbumControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
// Need to mock entity manager for doctrine use
$this->em = $this->getMock('EntityManager', array('persist', 'flush', 'find','getClassMetaData','getRepository'));
$this->em
->expects($this->any())
->method('persist')
->will($this->returnValue(true));
$this->em
->expects($this->any())
->method('flush')
->will($this->returnValue(true));
$this->em
->expects($this->any())
->method('find')
->will($this->returnValue(true));
$this->em
->expects($this->any())
->method('getRepository')
->will($this->returnValue(true));
$this->em
->expects($this->any())
->method('getClassMetadata')
->will($this->returnValue((object)array('name' => 'aClass')));
$this->doctrine = $this->getMock('Doctrine', array('getEntityManager'));
$this->doctrine
->expects($this->any())
->method('getEntityManager')
->will($this->returnValue($this->em));
$this->setApplicationConfig(
include __DIR__ . '../../../../../../config/application.config.php'
);
parent::setUp();
}
public function testIndexActionCanBeAccessed()
{
//$albumTableMock = $this->getMockBuilder('Album\Models\AlbumTable')
$albumTableMock = $this->getMockBuilder('Album\Entity\Album')
->disableOriginalConstructor()
->getMock();
$albumTableMock->expects($this->once())
//->method('fetchAll')
->method('findAll')
->will($this->returnValue(array()));
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Album\Entity\Album', $albumTableMock);
$this->dispatch('/album');
$this->assertResponseStatusCode(200);
$this->assertModuleName('Album');
$this->assertControllerName('Album\Controller\Album');
$this->assertControllerClass('AlbumController');
$this->assertMatchedRouteName('album');
}
I think I've set up the basics of an EntityManager mock although the references I based this off of had mocked a FakeRepository() object that they returned the the getRepository() method that wasn't shown. Also, I'm not sure how to call the mocked EntityManager in the test in place of the original getMock call.
You need to use GenericTestsDatabaseTestCase for database test cases and need to provide mysql dump file in xml format as dataset in database test case class. Due to this, during unit testing, application will not query on actual database, instead it'll query on mysql xml dump. Other things like Exception/Error in doctrine entity if there is not any matched columns etc.. will work as it is. In short, due to dataset [database xml dump file] doctine will fire query on xml dataset file instead of actual database.

Dynamic class name in PHP

I'm trying to create a system that it has a GeneralObj. The GeneralObj allows user to initiate special objects for database's tables with a String of the table name. So far, it works perfect.
class GeneralObj{
function __construct($tableName) {
//...
}
}
However, it is too tired to type new GeneralObj(XXX) every time.
I am wondering is that possible to simplify the process to like new XXX(), which is actually running the same as new GeneralObj(XXX)?
I spot PHP provided __autoload method for dynamic loading files in the setting include_path but it requires a the actually definition file existing. I really don't want to copy and copy the same definition files only changing a little.
For cause, eval is not an option.
Maybe you can just auto-create the files in the autoloader:
function __autoload($class_name) {
// check for classes ending with 'Table'
if (preg_match('/(.*?)Table/', $class_name, $match)) {
$classPath = PATH_TO_TABLES . '/' . $match[1] . '.php';
// auto-create the file
if (!file_exists($classPath)) {
$classContent = "
class $class_name extends GeneralObj {
public __construct() {
parent::__construct('{$match[1]}');
}
}";
file_put_contents($classPath, $classContent);
}
require_once $classPath;
}
}
Use inheritance. Make GeneralObj the superclass of the table specific classes. This way you can dynamically derive class names and instantiate objects. Example:
class someTable extends GeneralObj {
}
$tableName = 'some';
$className = $tableName . 'Table';
$obj = new $className;
No, this is not possible.
The runkit extension allows programmatic manipulation of the PHP runtime environment, but it cannot do this. Even if it could, it would IMHO be a very bad idea, greatly impacting the requirements and complexity of the application in exchange for saving a few keystrokes.
In an unrelated note, your GeneralObj class has functionality that sounds suspiciously like that of a dependency injection container. Perhaps you should consider replacing it with one?
Something like this autoloader:
myAutoloader::Register();
class myAutoloader
{
/**
* Register the Autoloader with SPL
*
*/
public static function Register() {
if (function_exists('__autoload')) {
// Register any existing autoloader function with SPL, so we don't get any clashes
spl_autoload_register('__autoload');
}
// Register ourselves with SPL
return spl_autoload_register(array('myAutoloader', 'Load'));
} // function Register()
/**
* Autoload a class identified by name
*
* #param string $pClassName Name of the object to load
*/
public static function Load($pClassName){
if (class_exists($pClassName,FALSE)) {
// Already loaded
return FALSE;
}
$pClassFilePath = str_replace('_',DIRECTORY_SEPARATOR,$pClassName) . '.php';
if (file_exists($pClassFilePath) === FALSE) {
// Not a class file
return new GeneralObj($pClassName);
}
require($pClassFilePath);
} // function Load()
}
And it's up to GeneralObj to throw an exception if the table class can't be instantiated

Categories