Hi Leo!

In catching up (been off the ZF scene for a while deep in client work) I was 
referred to the Zend_Di (Dependency Injection) proposal on the Wiki which IMO 
is quite possibly my most looked-forward-to component (besides Zend_Rtf) right 
now. I've requested the author append the Zend_Factory methods as a simple 
light alternative for those who don't intend using the full Zend_Di 
capabilities but still require the minimum amount of injection needed to 
substitute Mock Objects or Stubs without requiring configuration files. Seems 
the most suitable location other than Zend_Loader assuming Zend_Di is 
eventually accepted into the Incubator or that new Extras Incubator...

If anyone from Zend is reading, review already ;). It's a solid piece of work 
that is invaluable in applying TDD/BDD/unit testing in general to the ZF MVC 
architecture - and that's only the most obvious benefit from my selfish 
perspective!

Best regards,
Paddy
 
Pádraic Brady

http://blog.astrumfutura.com
http://www.patternsforphp.com
OpenID Europe Foundation


----- Original Message ----
From: Leo Büttiker <[EMAIL PROTECTED]>
To: Pádraic Brady <[EMAIL PROTECTED]>; Zend Framework General 
<fw-general@lists.zend.com>
Sent: Monday, January 21, 2008 4:21:06 PM
Subject: AW: [fw-general] Zend_Factory/Zend_Loader addition suggestion


Sounds cool! This would make testing in ZF much easier.

In my opinion the Zend_Factory should somehow be integrated into
 Zend_Loader, to integrate easy in auto loaded environments. Probably
 Zend_Factory can even be a solution to replace components in the system without
 subclassing and copying a lot of code. But for this it should be
 possible to replace classes not only by object but also by other names.

Cheers,
leo
________________________________________
Von: Pádraic Brady [mailto:[EMAIL PROTECTED]
Gesendet: Donnerstag, 17. Januar 2008 22:21
An: Zend Framework General
Betreff: [fw-general] Zend_Factory/Zend_Loader addition suggestion

Hi guys,

I'm currently working on a project to implement Zend Framework
 controller and model testing and specification. I'm looking for opinions on
 whether this is feasible - the main problem I have is that directly
 instantiating objects within controllers makes it impossible for any testing
 suite from isolating the controller action from it's carrying objects.
 Without requiring some perverse parameter acrobatics or requiring
 configuration heavy injectors like Pico or Phemto a simple solution (I
 think) is to enable a simplified object loader of a few dozen lines to be
 added for use.

This could be added either as a separate class Zend_Factory, or
 appended to Zend_Loader (which seems appropriate). I've copied the current
 sample code and unit tests below. In use, it would require a simple call
 of:

Zend_Factory::create('Zend_Mail');

Then a unit test or BDD spec could mock this object result (before the
 create() call of course) using:

Zend_Factory::replaceClass('Zend_Mail', new ZMail_Mock);

Here's the suggested source code and unit tests. Any comments or
 criticisms most welcome - not sure if it's worth an actual proposal given
 it's size but it's of immense value to my ongoing work. If you prefer
 online viewing it's at:
 
http://svn.astrumfutura.org/zendframework/trunk/library/Proposed/Zend/Factory.php

Zend/Factory.php:

<?php

require_once 'Zend/Loader.php';

require_once 'Zend/Registry.php';

/**
 * Abstract object instantiation with minimal setup and allow for
 replacement
 * of created object return values by preceding unit tests or BDD
 specifications
 *
 */
class Zend_Factory
{

    /**
     * Registry instance for holding replacement objects
     *
     * @var Zend_Registry
     */
    protected static $_registry = null;

    /**
     * Create a new object based on the referenced class name and
 construction
     * parameters. If a registered replacement object exists, this will
 be
     * returned instead.
     *
     * @param object $className
     * @param array $constructionParams
     * @return object
     */
    public static function create($className, array $constructionParams
 = null)
    {
        if (!class_exists($className, true)) {
            Zend_Loader::loadClass($className);
        }
        if (isset(self::$_registry->$className)) {
            return self::$_registry->$className;
        }
        if ($constructionParams !== null) {
            $refClass = new ReflectionClass($className);
            $createdObject =
 $refClass->newInstanceArgs($constructionParams);
        } else {
            $createdObject = new $className;
        }
        return $createdObject;
    }

    /**
     * Replace the return value of any call for an instance of the
 referenced
     * class name with an alternative object.
     *
     * @param string $className
     * @param object $withObject
     */
    public static function replaceClass($className, $withObject)
    {
        if (self::$_registry == null) {
            self::$_registry = new Zend_Registry(array(),
 ArrayObject::ARRAY_AS_PROPS);
        }
        self::$_registry->$className = $withObject;
    }

    /**
     * Clear the registry of replacement objects
     *
     */
    public static function clearRegistry()
    {
        if(isset(self::$_registry)) {
            self::$_registry = null;
        }
    }
}

Unit Tests: FactoryTest.php

<?php
/**
 * @package    Zend_Factory
 * @subpackage UnitTests
 */

require_once dirname(dirname(__FILE__)) . '/TestHelper.php';


/** Zend_Factory */
require_once 'Zend/Factory.php';


/** PHPUnit_Framework_TestCase */
require_once 'PHPUnit/Framework/TestCase.php';

// pre included class used in tests
require_once 'Zend/Mail.php';


/**
 * @package    Zend_Factory
 * @subpackage UnitTests
 */
class FactoryTest extends PHPUnit_Framework_TestCase
{

    public function testFactoryInstantiatesZendObjectAfterIncluded()
    {
        $mail = Zend_Factory::create('Zend_Mail');
        $this->assertTrue($mail instanceof Zend_Mail);
    }

    public function testFactoryInstantiatesZendObjectBeforeIncluded()
    {
        $gdata = Zend_Factory::create('Zend_Gdata_Photos_AlbumEntry');
        $this->assertTrue($gdata instanceof
 Zend_Gdata_Photos_AlbumEntry);
    }

    public function testFactoryThrowsExceptionIfClassDoesNotExist()
    {
        try {
           $mail = Zend_Factory::create('Zend_Foo');
           $this->fail('Did not throw an expected Exception on
 non-existent class request');
        } catch (Zend_Exception $e) {
        }
    }

    public function testFactoryInstantiatesUsingConstructParams()
    {
        $registry = Zend_Factory::create('Zend_Registry',
 array(array('index'=>'something')));
        $this->assertEquals('something', $registry['index']);
    }

    public function testFactoryAllowsObjectReplacementForClasses()
    {
        Zend_Factory::replaceClass('Zend_Registry', new Factory_Foo);
        $class = Zend_Factory::create('Zend_Registry');
        $this->assertTrue($class instanceof Factory_Foo);
    }

    public function
 testFactoryChecksOriginalClassExistenceToPreventBlindReplacement()
    {
        Zend_Factory::replaceClass('Zend_Foo', new Factory_Foo);
        try {
            $class = Zend_Factory::create('Zend_Foo');
            $this->fail('Expected exception since replaced class never
 existed which means replacement could create blind errors');
        } catch (Zend_Exception $e) {
        }
    }

    public function tearDown()
    {
       Zend_Factory::clearRegistry();
    }

}

class Factory_Foo
{
}

Best regards,
Paddy

Pádraic Brady

http://blog.astrumfutura.com
http://www.patternsforphp.com
OpenID Europe Foundation Member-Subscriber




Reply via email to