Category Archives: PhpUnit Tests

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)

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! 😀

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.

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…

Demolition Man: Deleting Data

Ok, we read data, we changed data. That’s enough for the constructive way. It’s time to destroy something. The strategy is quite simple: Create data, retrieve it from the database and destroy it, but now, of course, it is very important to check the database, if the data actually was deleted. The code is straight forward:


public function canDeleteAppointmentFromRepository() {
    $testId = 42;
    $uid = $this->setupTestData($testId);
    $appointment = $this->appointmentRepository->findByUid($uid);
    $this->appointmentRepository->remove($appointment);
    unset($appointment);
    $this->persistenceManager->persistAll();
    $appointment = $this->appointmentRepository->findByUid($uid);
    $this->assertNull($appointment, 'Appointment is not NULL after removing it from the repository');
}

Those who are very familiar to software engineering and British SF might find the answer to all questions in line #2. So you don’t need to read on, because this says it all. 🙂

One special thing to remark here is on line #7. In the lines above I just created and removed the Entity Object. Nevertheless the findByUid in line #8 would find the entity object in the database anyway, because it was marked as “deleted” in the repository’s cache, but was not physically removed from the DB. Thus we have to tell the Persistence Manager to update the database. This is done in line #7. What we need for this is an instance of the persistence manager. We get it by a dependency injection:


/**
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;

public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
    $this->persistenceManager = $persistenceManager;
}

protected function setUp() {
    parent::setUp();
    $this->testingFramework = new Tx_Phpunit_Framework('tx_csevents');
    $this->setupConfiguration();
    $this->appointmentRepository = $this->objectManager->get(Tx_Csevents_Domain_Repository_AppointmentRepository);
    $this->injectPersistenceManager($this->objectManager->get(Tx_Extbase_Persistence_Manager));
}

The first lines are just classic dependency injection: Declare a property to store the reference to the persistence manager and define a function starting with “inject” to inject the actual instance of it.

Now you can argue, if you want to do your injection within the constructor or within the setUp() method. I chose the setUp() method, because there are 3 constructors available, I have to overwrite, but only one setUp() method I can use as a centralized point for setting up all my environment and Testing Fixture. In my opinion it’s the better solution.

Change the Entity and Write it back to the Repository

When the read test was done successfully, doing the change/write test is simple:


public function canWriteChangedAppointmentToRepository() {
    $testId = 1511;
    $uid = $this->setupTestData($testId);
    $appointment = $this->appointmentRepository->findByUid($uid);
    $appointment->setInternalId('Changed internal id #' . $testId);
    $this->appointmentRepository->update($appointment);
    unset($appointment);
    $appointment = $this->appointmentRepository->findByUid($uid);
    $this->assertNotNull($appointment, 'Wasn\'t able to get back the changed data from the DB at all');
    $this->assertEquals('Changed internal id #' . $testId, $appointment->getInternalId(), 'Retrieved not the expected (changed) internal id from the DB.');

    $this->markTestIncomplete('Change more data?!');
}

Please note that in this case I don’t check, if the data is physically changed in the database. I just rely on the repository. If you want to test the database directly, just read on with the next post, which describes how to do this (using data deletion as an example).

Please also note line 12 where I mark the test as being “incomplete”. I was just struggling, if I should change more data of the entity object. This is (clearly on tests only) another way to mark some “to do-s” like I described in the “Refacoring – Avoiding the Frustration” post.

I read somewhere else in a testing blog that they use assertSame over assertEquals, because this checks the type to be the same as well. I’ll follow this recommendation and change my tests later on in this way.

DB-Test: Read

It’s time to read the first data from the database. Let’s develop a repository and retrieve the created test data from it after configuration is in place. The repository is just standard (i.e. an empty child of Tx_Extbase_Persistence_Repository.

The (empty) Repository ClassNow, if the data mapper configuration is fine and the entity object does it’s job, everything should be alright. Of course I just defined a “read from the database” testcase first:


public function canReadAppointmentFromRepository() {
    $testId = 1876;
    $uid = $this->setupTestData($testId);

    $appointmentsQueryResult = $this->appointmentRepository->findAll();
    $this->assertNotNull($appointmentsQueryResult, 'appointmentsQueryResult is not set');
    $this->assertInstanceOf(Tx_Extbase_Persistence_QueryResultInterface, $appointmentsQueryResult, 'appointmentsQueryResult is of wrong type');

    $appointment = $this->appointmentRepository->findByUid($uid);
    $this->assertNotNull($appointment);
    $this->assertInstanceOf(Tx_Csevents_Domain_Model_Appointment, $appointment);

    $this->assertEquals('Test Title #' . $testId, $appointment->getInternalId(), 'internalId does not match');
    $this->assertEquals('List as #' . $testId, $appointment->getListAs(), 'listAs does not match');
    $this->assertEquals($appointment->getEventStartTime() - 15*60, $appointment->getEventLetInTime(), 'letInTime does not match to startTime');
    $this->assertEquals($appointment->getEventStartTime() + 90*60, $appointment->getEventEndTime(), 'startTime does not match to endTime');
    $this->assertEquals(70, $appointment->getNumOfSeatsAvailable(), 'numOfSeatsAvailable does not match');
    $this->assertFalse($appointment->getIsSoldOut(), 'isSoldOut does not match');

    $this->markTestIncomplete('Repository is not completly tested yet. Link to Description is missing.');
}

So in the first place I create a DB test record and afterwards I just re-read it from the database and compare the entity object’s content with the data that was written to the database in the beginning.

You can of course use another approach by creating the database content through an newly created entity object that’s stored to the database by the repository itself. This means testing the writing (i.e. actually creating) and reading back in one step. I didn’t choose this way, because a) it would mix up two tests and b) I wouldn’t be sure if the data really comes from the database and not from an internal cache of the repository. To prevent b) I wanted to use physically existing data from the database and to ensure this without having a prepared set of test data stored in my database, I just created it on the fly using the phpunit testing framework.

Create Test Data in the Database

In order to test the physical and logical database connect, I don’t use Mockups here (we’ll do that later on), but I create database test data via an instance of the phpunit Test Framework in the setUp() method (see the prior post). Later on (while testing) I can use this data to read it from the database, change it and store it back into the database. While doing this I will check the correctness and consistency of all transferred data.

To create unique test data, I simply use a unique id that defines the data:


protected function setupTestData($number) {
    $now = time();
    $uid = $this->testingFramework->createRecord(
        'tx_csevents_appointments',
        array(
            'internal_id' => 'Test Title #' . $number,
            'listas' => 'List as #' . $number,
            'stime' => $now,
            'letin' => $now - 15*60,
            'etime' => $now + 90*60,
            'places' => 70,
            'soldout' => 0
        )
    );
    return $uid;
}

This is simple, but very efficient. You can use more complex test data generators (e.g. with more varying parameters) to achieve a better test coverage. Think of equivalent class testing for instance.

Note for those, who are not that familiar with testing strategies: If you want to understand Equivalent Class Testing just think of a field where it’s required to enter a price between 0 and 1.000,-€ (including). To test this field, you should test the following values:

  • -1,-€ shall fail
  • 0,-€ shall succeed
  • 1,-€ shall succeed
  • some random value between 1,- and 1.000,-€ shall succeed
  • 999,-€ shall succeed
  • 1.000,-€ shall succeed
  • 1.001,-€ shall fail

…and of course you can just drill this down to € Cents as well – and do tests for $ additionally. There is a lot of room for improvement, but this will tell you the basic idea of Equivalent Class Testing: Test the borders of allowed / valid values from both sides (the allowed and the discarded side) and don’t forget to test the border itself as well (e.g. “0€” including or excluding?!). This is, where most of the GUI defects occur.