Category Archives: Source Code

Mocking the ObjectManager: Pitfall ahead!

If you mock up the ObjectManager, be aware of a very annoying pitfall, that might drive you nuts as long as you are not aware of it (and maybe even, if you already know about it)…

The following code works as expected:

/**
 * @test
 */
public function myControllerTestWorksFine() {
    $mockObjectManager = $this->getMock(Tx_Extbase_Object_Manager, array(), array(), '', FALSE);
    $this->myController->injectObjectManager($mockObjectManager);
    $mockObjectManager->expects($this->at(0))
        ->method('get')
        ->with(SomeClassToConstruct)
        ->will($this->returnValue($someMockedupClassToReturn));
    $this->myController->indexAction();
}

The (abbreviated) code will simply build a mockup for the ObjectManager, inject this mock into the controller, add an expectation together with a ‘with’ and ‘will’ statement and call the controller’s index action. Fine. The controller will at some point call the ObjectManager to get an instance of ‘SomeClassToConstruct’ and will get the defined result.

Now let’s change the order of the inject- and expect-statements:

/**
 * @test
 */
public function myControllerTestWorksFine() {
    $mockObjectManager = $this->getMock(Tx_Extbase_Object_Manager, array(), array(), '', FALSE);
    $mockObjectManager->expects($this->at(0))
        ->method('get')
        ->with(SomeClassToConstruct)
        ->will($this->returnValue($someMockedupClassToReturn));
    $this->myController->injectObjectManager($mockObjectManager);
    $this->myController->indexAction();
}

This won’t work like expected and will lead to a very hard to find error. Suddenly the parameters sent to the ObjectManager’s get-method don’t match the expected once any more. The reason: There is a hidden get-call added, you cannot see. To get this work again, you have to change ‘$this->at(0)’ to a ‘$this->at(1)’, because of the additional (unknown but counted) get-statement. This is ugly.

To avoid situations like these (and searching for the error / the solution for hours), use this coding guideline: Whenever you inject a mock object into another class, do it directly after generating the mock without any other statement inbetween. ($this->getMock directly followed by the inject-Statement)

Test the Test

Just a recommendation and a “best practise” coming from my current daily work on Test First Development. I wanted to introduce a new ValueObject, thus I started to write the Unit Test for it. It looked like this in my IDE (Eclipse; pseudo code used):

Bildschirmfoto vom 2014-12-17 21:43:52This is the source code:

class MyTest {
    public function checkDefaultValues() {
        $myValueObject = $this->createValueObject();
        $this->assertInstanceOf(Tx_Extbase_DomainObject_AbstractValueObject, $myValueObject, 'ValueObject does not implement AbstractValueObject');
        $this->assertABC(/*...*/);
    }

    /**
     * @test
     */
    public function checkSetAndGetPropertyA() {
        // ...
    }

    /**
     * @test
     */
    public function checkSetAndGetPropertyB() {
        // ...
    }
}

After i wrote the test like quoted above I tended to implement the ValueObject without any prior test run, because I was just very sure about what’s to be done and it was quite abvious that the test will fail very fast without any testable class in place. Fortunately I controled my feelings and had a break. I just started the test and did the implementation step by step based on the upcoming error messages. I was astonished! Why? Read on…

The very first test run failed, because no ValueObject class was available at all. That’s no rocket sience. So I did the first very, very basic implementation of “ValueObject”. It looked like this:

class ValueObject {
}

Simple. Important: I did not extend the ValueObject from the base class Tx_Extbase_DomainObject_AbstractValueObject on purpose to find out, if the “assertInstanceOf” of my test (highlighted in line #4 of the test case implementation) would fire an assertion failure. Surprise: It didn’t!

Why? Because “checkDefaultValues” was never executed at all. And that is, because I forgot to add the “@test” annotation for this test. I wasn’t aware of this bug, because the annotations were minimised in my IDE view (have a look at the screenshot on top of this post).

If I would have implemented the ValueObject in the usual way using a copy&paste of another value object or by adding the extension in the very first implementation, I wouldn’t have seen this flaw in the test.

Outcome: “Test First Development” means also: “Implementation driven by test defects.” This will test your tests and will let you have better tests and better software. The only thing on your side: You have to be disciplined… Right after this experience I won’t do it differently any more. 😀

ValueObjects stored as properties in other ValueObjects

As I mentioned in Testing and Coding a ValueObject it is necessary to clone an object, whenever you store it to or retrieve it from a ValueObject in order to make the ValueObject immutable. Just to add one fundamental topic to this general policy: Whenever you store a ValueObject (InnerVO) as a property inside another ValueObject (VOContainer), you don’t have to do this cloning process at all.

class InnerVO extends Tx_Extbase_DomainObject_AbstractValueObject {
    // ...
}

class VOContainer extends Tx_Extbase_DomainObject_AbstractValueObject {
    /**
     * @var InnerVO
     */
    protected $innerVO;

    /**
     * @param InnerVO $innerVO
     * @return void
     */
    public function __construct($innerVO) {
        $this->innerVO = $innerVO;
    }

    /**
     * Read the innerVO
     * @return InnerVO The innerVO attribute
     */
    public function getInnerVO() {
        return $this->innerVO;
    }
}

Why not cloning?

As long as you follow the formerly discussed policy to make each and every ValueObject immutable, you don’t have to clone it at all while throwing it all over your sourcecode, because it will never change. This is one of the major advantages of making a ValueObject immutable and ensure it’s immutability by forbidding each and every setter method.

The other way around: Whenever you want to change the value of the innerVO object (see above) stored inside VOContainer, you have to create a new innerVO object holding the new values. And because VOContainer is a ValueObject and therefor it’s immutable, you have to build a new VOContainer as well, because the VOContainer’s innerVO cannot be overwritten. There is no setter method and VOContainer must be immutable as well.

Big challenge: Selenium causes the ObjectManager to fail

It might happen (in my case it did) that running Selenium tests will cause your ObjectManager to fail. After (!) the first Selenium test run, the ObjectManager or some other instance that uses the $GLOBALS[‘TYPO3_DB’] connection might fail like this:

Bildschirmfoto vom 2014-10-20 21:43:31I simply wrapped my ObjectManager call into a try-catch-block to get some more details about the issue. This is the stacktrace (you might face something similar):

#0 [internal function]: PHPUnit_Util_ErrorHandler::handleError(2, 'mysql_real_esca...', '/opt/typo3_src-...', 774, Array)
#1 [...]t3lib/class.t3lib_db.php(774): mysql_real_escape_string('Tx_Extbase_MVC_...', 0)
#2 [...]t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php(205): t3lib_DB->fullQuoteStr('Tx_Extbase_MVC_...', 'tx_extbase_cach...')
#3 [...]t3lib/cache/frontend/class.t3lib_cache_frontend_abstractfrontend.php(103): t3lib_cache_backend_DbBackend->has('Tx_Extbase_MVC_...')
#4 [...]typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php(59): t3lib_cache_frontend_AbstractFrontend->has('Tx_Extbase_MVC_...')
#5 [...]typo3/sysext/extbase/Classes/Object/Container/Container.php(289): Tx_Extbase_Object_Container_ClassInfoCache->has('Tx_Extbase_MVC_...')
#6 [...]typo3/sysext/extbase/Classes/Object/Container/Container.php(127): Tx_Extbase_Object_Container_Container->getClassInfo('Tx_Extbase_MVC_...')
#7 [...]typo3/sysext/extbase/Classes/Object/Container/Container.php(95): Tx_Extbase_Object_Container_Container->getInstanceInternal('Tx_Extbase_MVC_...', Array)
#8 [...]typo3/sysext/extbase/Classes/Object/ObjectManager.php(83): Tx_Extbase_Object_Container_Container->getInstance('Tx_Extbase_MVC_...', Array)
#9 [...]typo3/sysext/extbase/Classes/MVC/Controller/AbstractController.php(167): Tx_Extbase_Object_ObjectManager->create('Tx_Extbase_MVC_...')
#10 [...]typo3conf/ext/csevents/Tests/Unit/Controller/AppointmentControllerTest.php(51): Tx_Extbase_MVC_Controller_AbstractController->injectObjectManager(Object(Tx_Extbase_Object_ObjectManager))
#11 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php(828): Tx_Csevents_Tests_Unit_Controller_AppointmentControllerTest->setUp()
#12 [...]typo3/sysext/extbase/Tests/Unit/BaseTestCase.php(62): PHPUnit_Framework_TestCase->runBare()
#13 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestResult.php(648): Tx_Extbase_Tests_Unit_BaseTestCase->runBare()
#14 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestCase.php(776): PHPUnit_Framework_TestResult->run(Object(Tx_Csevents_Tests_Unit_Controller_AppointmentControllerTest))
#15 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php(775): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#16 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php(745): PHPUnit_Framework_TestSuite->runTest(Object(Tx_Csevents_Tests_Unit_Controller_AppointmentControllerTest), Object(PHPUnit_Framework_TestResult))
#17 [...]typo3conf/ext/phpunit/Composer/vendor/phpunit/phpunit/PHPUnit/Framework/TestSuite.php(705): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult), false, Array, Array, false)
#18 [...]typo3conf/ext/phpunit/Classes/BackEnd/Module.php(831): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult))
#19 [...]typo3conf/ext/phpunit/Classes/BackEnd/Module.php(593): Tx_Phpunit_BackEnd_Module->runAllTests(Object(PHPUnit_Framework_TestSuite), Object(PHPUnit_Framework_TestResult))
#20 [...]typo3conf/ext/phpunit/Classes/BackEnd/Module.php(288): Tx_Phpunit_BackEnd_Module->renderRunningTest()
#21 [...]typo3conf/ext/phpunit/Classes/BackEnd/Module.php(206): Tx_Phpunit_BackEnd_Module->renderRunTests()
#22 [...]typo3conf/ext/phpunit/Classes/BackEnd/index.php(86): Tx_Phpunit_BackEnd_Module->main()
#23 [...]typo3/mod.php(51): require('/home/.sdb/var/...')
#24 {main}

It’s obvious that t3lib_db does not have any link to the database after a Selenium run (look at the last “0” parameter at #1, line 2). This link is set up by t3lib_db::sql_pconnect(…) and used by calling $GLOBALS[‘TYPO3_DB’]. That’s where this database connection is stored.

The root cause for this failure is inside the PHPUnit_Framework_TestCase class that runs all tests. In some circumstances (I’ll come to the details later on) it backups the $GLOBALS before running a test and restores them back right after the test was executed. It looks like this:

public function runBare()
{
    $this->numAssertions = 0;

    // Backup the $GLOBALS array and static attributes.
    if ($this->runTestInSeparateProcess !== TRUE &&
        $this->inIsolation !== TRUE) {
        if ($this->backupGlobals === NULL ||
            $this->backupGlobals === TRUE) {
            PHPUnit_Util_GlobalState::backupGlobals(
                $this->backupGlobalsBlacklist
            );
        }

        if ($this->backupStaticAttributes === TRUE) {
            PHPUnit_Util_GlobalState::backupStaticAttributes(
                $this->backupStaticAttributesBlacklist
            );
        }
    }

// more test preparation (code skipped...)

    $this->testResult = $this->runTest();

// shutting down the test (code skipped...)

    // Restore the $GLOBALS array and static attributes.
    if ($this->runTestInSeparateProcess !== TRUE &&
        $this->inIsolation !== TRUE) {
        if ($this->backupGlobals === NULL ||
            $this->backupGlobals === TRUE) {
            PHPUnit_Util_GlobalState::restoreGlobals(
                $this->backupGlobalsBlacklist
            );
        }

        if ($this->backupStaticAttributes === TRUE) {
            PHPUnit_Util_GlobalState::restoreStaticAttributes();
        }
    }

// even some more cleanup activities (code skipped...)
}

You see some if-statements steering, if the backup/restore is done or not. In case of my Selenium testcases, the backup/restore was performed, i.e. the following parameters are set:

$this->runTestInSeparateProcess = NULL; // default; = FALSE would be the same
$this->inIsolation = NULL; // default; = FALSE would be the same
$this->backupGlobals = NULL; // default; = TRUE would be the same
$this->backupGlobalsBlacklist = array(); // default

With this setting, the TestCase tries to backup and restore all $GLOBALS values including the $GLOBALS[‘TYPO3_DB’] connection. PHPUnit_Util_GlobalState does this by serialising and deserialising this resource, which in case of the DB-connection does not work. Right after the restore $GLOBALS[‘TYPO3_DB’] is set to 0.

To avoid this, a) someone has to fix the restore-procedure or b) you have to avoid the backup/restore of the DB-connection. Options for b) directly come from the parameters above:

  1. Run the test in a separate process (to be described later on) or…
  2. Set the “backupGlobals” switch to “FALSE” in order to not backup/restore any global variables (which might lead to other problems, if your tests change global values) or…
  3. Exclude the TYPO3_DB variable from being backuped/restored by adding it to the blacklist.

I found a Typo3 Forum post about this issue: http://forum.typo3.org/index.php/t/140961/ It recommends to use solution #3. It works!!!

class Tx_Csevents_Tests_Selenium_BasicSeleniumTestCase extends Tx_Phpunit_Selenium_TestCase {

    // add "TYPO3_DB" to the global backup black list in order to not lose the DB connection
    protected $backupGlobalsBlacklist = array('TYPO3_DB');

    //.... (followed by the rest of this class)
}

That’s it: I already introduced a “BasicSeleniumTestCase” class to handle all Selenium Test cases. Now I use this class to overwrite the $backupGlobalsBlacklist, which is empty by default, using a list that contains “TYPO3_DB”. Now $GLOBALS[‘TYPO3_DB’] is not backuped nor restored any more, thus it’s save from being overwritten by 0 (because resource values are not serializable). Finally fixed! 😀

ValueObjects with lots of (optional) objects

If you want to define ValueObjects with a lot of optional objects using the implementation described in Making a ValueObject NULL-prove, this will lead to a very long constructor and a lot of copy / paste code. Not nice.

Solution #1: divide et impera

The very first idea is to define private setters for each object. These setters are called by the constructor only and every setter is responsible for one object only. This will reduce the length of the constructor code, but it won’t solve the copy-paste issue.

Solution #2: Define a generic (optional) object setter

class MyValueObject {
    protected $param1;
    protected $param2;
    protected $param3;
    protected $param4;
    protected $param5;

    public function __construct($param1, $param2, $param3, $param4, $param5) {
        $this->setParam('param1', $param1, DateTime);
        $this->setParam('param2', $param2, SomeOtherClass);
        //....
    }

    private setParam($name, $value, $class) {
        if($value === NULL) {
            $this->$name = NULL;
        } elseif($value instanceof $class) {
            $this->$name = clone $value;
        } else {
            throw new InvalidArgumentException('expecting parameter ' . $name . ' to be of type ' . $class, 1413833055);
        }
    }
}

Now you can use setParam(…) to set all internal objects including cloning, NULL values (you might switch this functionality on/off for certain parameters to make them mandatory) and type/class checks. This reduces your code, because your constructor uses just one line of code per parameter and your setter is defined just once, thus you don’t do copy/paste and can rely on the centralised functionality. Nice? Nice.

Making a ValueObject NULL-prove

in Testing and Coding a ValueObject we used “clone” to make the ValueObject and it’s internally stored objects immutable. The code for the constructor and the getter was quite simple:

/**
 * @var DateTime
 */
protected $dateTime;

/**
 * @param DateTime $dateTime
 */
public function __construct($dateTime) {
    $this->dateTime = clone $dateTime;
}

Unfortunately this leads to an exception, if you try to set NULL. Furthermore it doesn’t take care of wrong parameters like e.g. an integer, array or objects different from DateTime. To make this ValueObject “NULL-prove” and check the argument’s type, we generate an according test first.

/**
 * @test
 */
public function setAndGetNullWorks() {
    $myValueObject = $this->objectManager->get(MyValueObject, NULL);
    $this->assertNull($myValueObject->getDateTime());
}

/**
 * @test
 * @expectedException InvalidArgumentException
 * @expectedExceptionCode 1412287860
 */
public function constructWithNonDateTimeThrowsException() {
    $myValueObject = $this->objectManager->get(MyValueObject, 42);
}

The first test just calls the constructor with $dateTime=NULL and expects the same “NULL”, if it reads this value back using the getDateTime() method. Using the simple clone implementation above this will fail, because “clone NULL” does not work and will throw an exception.

The second test tries to send an int to the constructor. Please note the annotations: We expect the InvalidArgumentException to be thrown with a specific exception code (1412287860). This will fail as well, if we try to clone the int value.

You can use the constructor with an explicitly named Class like this:

public function __construct(DateTime $dateTime) {
    // ....
}

But this won’t let you use “NULL”, because “NULL” is not accepted as a DateTime object.

Conclusion

Do not use dedicated Class references in parameter lists and use class / type checks inside your value objects, if you

  • want to make the parameter optional (thus it might be “NULL”) or
  • want to use parameters that might be of different classes and/or types (“mixed”).

Do use dedicated Class references in parameter lists, if you

  • want to make the parameter mandatory and
  • want the parameter to be an object of a dedicated class.

You have to ask yourself…

Of course you have to ask yourself how a method shall react in cases like the once above. In my case I accept NULL parameters and I want to get an exception, if the parameter is not a DateTime object. It’s a very good idea to first think about your expectations and to write a unit test reflecting exactly those expectations and not thinking about the actual implementation in the first place. Do the implementation afterwards to meet the functional test, but don’t write a test to cover your planned technical implementation.

The implementation:

One of a variety of solutions to cover my tests above is the following implementation for the constructor:

/**
 * @param DateTime $dateTime
 */
public function __construct($dateTime) {
    if(is_null($dateTime)) {
        $this->dateTime = NULL;
    } elseif($dateTime instanceof DateTime) {
        $this->dateTime = clone $dateTime;
    } else {
        throw new InvalidArgumentException('MyValueObject::__construct must retrieve a DateTime object', 1412287860);
    }
}

One remark regarding the exception code: It’s pretty equal which exception code you use. I totally recommend to use exception codes and to use unique exception codes for every position in the source code where an exception is thrown. Reason: It’s very easy to find those exception locations by searching for the exception code using a simple full text search. This makes support and debugging much more easier.

If you want to generate a unique exception code, just use the current system time stamp when writing your code (e.g. “date +%s” for Unix/Linux systems). I always define the exception code in the test that expects the exception to be thrown, because this is the very first place where the exception code is used in the “@expectedExceptionCode” annotation. The actual implementation just copies this value to meet the test case’s expectation.

Testing and Coding a ValueObject

A simple ValueObject (which is not an Entity, but holds non-persistent data only) has to be immutable to use it properly. It’s the basic idea behind a ValueObject that it’s data stays the same for all of it’s lifetime.

Question #1: What is “immutable” and what is it good for?

“Immutable” simply guaranties, that you will always read the same values from the same ValueObject. The content of the object will never change while your business logic is running. Whenever you need to change some values, you need to create a new ValueObject storing this changed data. Simply: same object === same data. This makes coding very easy and avoids critical and hard-to-find side effects. Reading this in the other way also means: You don’t need to create two different ValueObjects for the same data. Whenever the data is the same, simply use the same ValueObject. [Annotation: This makes garbage collection easy in the background as well.]

Think of this situation: You generate a ValueObject and you send it to a function to do *something*. You don’t know (or you don’t want to care about) the actual implementation of this function. If you want to use this ValueObject later on in your code, you have to trust this function that it doesn’t change your ValueObject. “Trust” is not a good concept in software development, “ensure” is much better. Thus: Make the ValueObject immutable in order to guarantee that it cannot be changed by any unknown / undefined / changing functionality.

Question #2: What’s about setter-Methods?

If you want to make the ValueObject actually immutable, you won’t use any setter-methods at all, because they are supposed to change the content of the ValueObject. Thus you have to send every data to the object’s constructor using a constructor parameter list. Of course you can still use setters, but then you have to ensure (“trust”?!) that some coding guidelines are used that prevent the object from being changed unintentionally. You need to decide on your own, which approach to use. I decided for my source code to use the constructor method. You can use this together with extbase’s ObjectManager as well, because the ObjectManager’s “get” method will route every additional parameter to the called constructor, even if the actual “get”-definition declares the class name parameter only.

Question #3: How do I achieve immutability for objects inside the ValueObject?

It’s very easy to achieve immutability for primitive data types: Just don’t define (or call) setters. Done. It’s more difficult for objects (like a DateTime object for instance) stored inside a ValueObject to stay immutable. Have a look:

class MyValueObject extends Tx_Extbase_DomainObject_AbstractValueObject {
    /**
     * @var DateTime
     */
    protected $dateTime;

    /**
     * @param DateTime
     */
    public function __construct($dateTime) {
        $this->dateTime = $dateTime;
    }

    public function getDateTime() {
        return $this->dateTime;
    }
}

Here you find the property $dateTime storing a DateTime object inside the ValueObject. Usually you would write a very simple getter method like the one above. Let’s have a look at it again:

public function getDateTime() {
    return $this->dateTime;
}

If you retrieve the $dateTime property in this way, you can change it’s value from the outside of the ValueObject without using any setter method:

$myValueObject = $this->objectManager->get(MyValueObject, new DateTime('2014-9-1 11:12:13'));

$localDateTime = $myValueObject->getDateTime();
$localDateTime->modify('+1 day');

// $localDateTime AND $myValueObject->dateTime changed to '2014-9-2 11:12:13'

This will change the $localDateTime value and add one day to the DateTime timestamp, but because $localDateTime is a simple reference to myValueObject’s $dateTime property, it will change the data of the $dateTime property (i.e. the content of the ValueObject) as well. This will lead to side effects, if you rely on your ValueObject to be immutable. Whenever you call getDateTime() later on, you will retrieve a DateTime object with an offset of 1 day to the original (expected) one. A hard-to-find-and-don’t-let-you-sleep-at-night bug. 🙁

Test first

Before we proceed with the solution, we will write a unit test first, because we want to follow the Test First Approach and we want to make sure that we do exactly the right things to achieve our test goals. The test runs like this:

  1. Setup a ValueObject with $dateTime set to a certain date/time.
  2. Get $dateTime from the ValueObject.
  3. Change (i.e. “->modify”) the retrieved DateTime object outside the ValueObject.
  4. Get $dateTime again from the ValueObject and compare it to the modified DateTime object.
  5. Goal: a) they must differ or b) getDateTime() must return the same (immuted) DateTime in the second call.

I’ll go for goal 5b).

Code:

$myValueObject = $this->objectManager->get(MyValueObject, new DateTime('2014-9-1 11:12:13'));
$localDateTime = $myValueObject->getDateTime();

// modifies the retrieved object, but MUST NOT modify the content ($dateTime property) of the ValueObject
$localDateTime->modify('+1 day'); 

$this->assertEquals(new DateTime('2014-9-1 11:12:13'), $myValueObject->getDateTime());

Using the simple getter from above, this test will fail, because “->modify” will change the $dateTime property of the ValueObject as well.

Please note: I used a completely new DateTime object in the assertion at the end of the test. I did this on purpose just to ensure, that I don’t compare an accidentally muted object to an accidental copy of the muted object. It makes the code easier to read and understand and it makes the test safer against any side effects, even if you have to generate a potentially unnecessarily additional object. (Developers don’t want to waste memory by generated unnecessary additional objects, but in this case it is worth the effort.)

Coding the ValueObject (part 1, getter):

Changing the getDateTime() method to secure it’s $dateTime property from being modified from the outside is very simple. Just clone the object before returning it:

public function getDateTime() {
    return clone $this->dateTime;
}

Now you can modify the DateTime object outside the ValueObject without any influence to the $dateTime property.

Challange #2:

Now we’ve created a unit test to prove that the getter method returns an independent object in order to make the inner property immutable. But that’s only half way to the goal. Think of this initialisation:

class MyValueObject {
    //...
    public function __constructor($dateTime) {
        $this->dateTime = $dateTime;
    }
}

$localDateTime = new DateTime('2014-5-13 7:12:17');
$myValueObject = $this->objectManager->get(MyValueObject, $localDateTime);
// more code...
$localDateTime->modify('+1 day');
// even more code...

Which date will be stored in the $dateTime property of the ValueObject at the end of this code? It will be “2014-5-14 7:12:17″. Why? The DateTime object $localDateTime was sent to the ValueObject’s constructor. The constructor stores the reference to this object inside the ValueObject. Whenever the original object is changed (“$localDateTime->modify…”) the reference will point to this changed object. Thus the content of the ValueObject will be changed as well. And finally this means: The ValueObject is not immutable (and that is independent of the strategy to use a setter or a constrctor argument, see above…).

Coding the ValueObject (part 2, setter/constructor):

To avoid this (and the resulting side effects) do what? Of course: Do the test first! Create a ValueObject sending an object to the constructor, change the object and check, if the getter still returns the original values.

        $localDateTime = new DateTime('2014-4-3 1:2:3');
        $myValueObject = $this->objectManager->get(MyValueObject, $localDateTime);
        $localDateTime->modify('+1 day');
        $this->assertEquals(new DateTime('2014-4-3 1:2:3'), $myValueObject->getDateTime());

This will fail for the upper implementation of the ValueObject. Thus we need to make the content of the ValueObject immutable by cloning it inside the constructor as well. Let’s have a look:

class MyValueObject {
    //...
    public function __constructor($dateTime) {
        $this->dateTime = clone $dateTime;
    }
}

Now $this->dateTime is completely independent of all things that happen outside the MyValueObject and the test will end successfully. Let’s sum this up into one test an one ValueObject:

class MyValueObjectTest extends Tx_Extbase_Tests_Unit_BaseTestCase {
    /**
     * @test
     */
    public function clonesCorrectlyAndIsImmutable() {
        $localDateTime = new DateTime('2014-4-3 1:2:3');
        $myValueObject = $this->objectManager->get(MyValueObject, $localDateTime);

        $myValueObject->getDateTime()->modify('-1 day');
        $this->assertEquals(new DateTime('2014-4-3 1:2:3'), $myValueObject->getDateTime(), 'getDateTime() does not clone correctly');

        $localDateTime->modify('+1 day');
        $this->assertEquals(new DateTime('2014-4-3 1:2:3'), $myValueObject->getDateTime(), 'Constructor does not clone correctly');
 }
}
class MyValueObject extends Tx_Extbase_DomainObject_AbstractValueObject {
    /**
     * @var DateTime
     */
    protected $dateTime;

    /**
     * @param DateTime $dateTime
     * @return void
     */
    public function __construct($dateTime) {
        $this->dateTime = clone $dateTime;
    }

    /**
     * @return DateTime
     */
    public function getDateTime() {
        return clone $this->dateTime;
    }
}

The implementation of the ValueObject hasn’t been finished yet, because it does not accept NULL parameters. Please have a look at the “Making a ValueObject NULL-prove” post for more information.

AutoLoader Effect #2: Foreign Sources

I started experimenting with Selenium testcases. Therefor I wanted to use Extbase’s class “SeleniumBaseTestCase” as a root class for all my test cases. This wasn’t possible, because of AutoLoader Effect #1. The autoloader was not able to find this class, because it wasn’t part of Extbase’s “/Classes” directory and it also wasn’t mentioned in Extbase’s “/ext_autoload.php” file.

Well, thinking of AutoLoader Effect #1‘s solution, I simply added an according line to my own “/ext_autoload.php” file, in order to tell the autoloader where to find “SeleniumBaseTestCase”.


$extensionClassesPath = t3lib_extMgm::extPath('csevents') . 'Classes/';
return array(
    'tx_extbase_tests_seleniumbasetestcase' => t3lib_extMgm::extPath('extbase') . 'Tests/SeleniumBaseTestCase.php',
    'tx_csevents_tests_unit_domain_repository_repositorybasetestcase' => $extensionClassesPath . '../Tests/Unit/Domain/Repository/RepositoryBaseTestCase.php',
);

Effect: None – same error, the class cannot be found by the autoloader.

Reason: The autoloader splits the class name, it is searching for, by “_” and searches for the second part, which holds the extension name (“extbase” or “csevents” in my cases). Afterwards it searches for the “ext_autoload.php” file in the extension’s root directory. Thus, searching for “Tx_Extbase_…” makes the autoloader search for “ext_autoload.php” in the “extbase” directory, “my” ext_autoload is never used while searching for “Tx_Extbase_…”. Even if I define the correct solution, the autoloader will never listen to it. Line #3 has absolutly no effect as long as it is not part of the “extbase” extension. 🙁

Solution: None by myself. The extension itself (extbase in this case) has to define all necessary autoload-entries. In this particular case, I was glad that “SeleniumBaseTestCase” is a 1:1 extension of “tx_phpunit_selenium_testcase”, which can be found easily by the autoloader. Thus I used that one. Not nice, but successfull.


<?php

/**
* Base Selenium testcase for the Extbase extension.
*/
abstract class Tx_Extbase_SeleniumBaseTestCase extends tx_phpunit_selenium_testcase {
}
?>

AutoLoader effect #1: “/Tests” Folder

There are some effects of the Extbase AutoLoader, you shall be aware of when writing unit tests.

Testing the repository, I defined a “RepositoryBaseTestCase” which holds common functionality for all 4 repository tests. Simple. Problem: The autoloader was not able to find this class when I tried to extend it.

class Tx_Csevents_Tests_Unit_Domain_Repository_AppointmentRepositoryTest extends Tx_Csevents_Tests_Unit_Domain_Repository_RepositoryBaseTestCase {
}

The base class “Tx_Csevents_Tests_Unit_Domain_Repository_RepositoryBaseTestCase” is on the same directory, but the autoloader is not able to find it. Whenever I try to run the tests, I get the error message “Fatal error: Class ‘Tx_Csevents_Tests_Unit_Domain_Repository_RepositoryBaseTestCase’ not found in /home/.sdb/var/www/html/dummy-4.5.32/typo3conf/ext/csevents/Tests/Unit/Domain/Repository/AppointmentRepositoryTest.php on line 37” Why is that? Simply, it’s because my unit tests are not stored underneath “/Classes”, but underneath “/Tests” (you see the referred base class on the bottom of the screenshot).

screenshot1The autoloader recongnises classes in the “/Classes” folder only, thus it doesn’t find the “RepositoryBaseTestCase.php” when I refer to it as “Tx_Csevents_Tests_Unit_Domain_Repository_RepositoryBaseTestCase”.

Solution: Help the autoloader and give it a file “/ext_autoload.php”:

screenshot2


<?php

$extensionClassesPath = t3lib_extMgm::extPath('csevents') . 'Classes/';
return array(
    'tx_csevents_tests_unit_domain_repository_repositorybasetestcase' => $extensionClassesPath . '../Tests/Unit/Domain/Repository/RepositoryBaseTestCase.php',
);

?>

Now the autoloader knows where to search for the referred class. Please note that you have to use lowercase letters for the array keys until Typo3 4.5.x including.

Using the “extensionClassesPath” with the trailing “Classes/” directory, which is removed by the “../” in the array again, is a little strange. I just used it, because this annotation is used commonly. It reflects the view of the autoloader, which is using “Classes” as it’s base directory to start searching. You may change this, if you feel better then. 😉

You could have done a simple “require_once” instead of all this stuff, but this is forbidden by the coding guidelines. Thus this is the official and nice solution.

Feeling good…

Let’s celebrate our achievements. Just run all tests and have a look at the result:

All green, tests doneThat is a very pleasant and comforting feeling. Have a look at the stats: I made a test of the complete Domain Model and of one Repository, checking all getters, setters, the data mapper configuration and the database treatment. That are 25 tests done. And they will be done every time I just start the unit test – and give me the very good feeling that my fundamental data structure and it’s treatment is still working. That lets you concentrate on your complex business middleware job without hesitating about the over all result in the end. That is indeed a very good status to go to bed and sleep well without having thousands of open points rushing though your mind and keeping you awake.

Please note the two grey bars there, telling me that I haven’t finished implementing all tests I already planned to do. Yes, that’s an open point, but I’m aware of it, the system supports me by granting this information to me and I will go on to fill these gaps asap. Nice to have a scout like this…