Monthly Archives: December 2014

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.

locallang with Typo3 4.5

Location of the locallang file

Some offical documentation does not refer to the locallang solution for Typo3 4.5 any more. In other word’s: It’s a little tricky nowadays to find out about how this works, but don’t panic, it’s simple: Locallang (the storage of translated terms for the module and the frontend) must be located at Resources/Private/Language/locallang.xml to be recognised by the extbase framework. This is simple, if you know it. 😉

Format of the locallang.xml

Secondly you have to concider the locallang.xml format to make things work. As long as the official documentation is available, you’ll be happy with it and it’s “Elements” link at the end of the page.

Be aware: In the moment you change from 4.5 to a higher version (4.6+), all this is void, because the localisation feature changes. If you want to work with 4.5 (LTS) as an intermediate step to a 4.6 or 6.2 version, you shouldn’t spend too much effort on the complete file structure of the locallang.xml, because it will be overwritten by the new framework very fast.

To define just as much as minimally necessary, your locallang.xml file should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<T3locallang>
    <meta type="array">
        <type>module</type>
        <description>Language labels for the CS Events plugin</description>
        <labelContext type="array"/>
    </meta>
    <data type="array">
        <languageKey index="default" type="array">
            <label index="CsEvents.appointmentListEmpty">Sorry, currently no events are available.</label>
        </languageKey>
        <languageKey index="de" type="array">
            <label index="CsEvents.appointmentListEmpty">Zur Zeit sind leider keine Termine verfügbar.</label>
        </languageKey>
    </data>
</T3locallang>

As you can see, I define the <meta> and <data> elements only and I totally ignore the <orig_hash> and <orig_text> elements here. Those elements are used for distributed translations. You don’t need them, if you don’t want anybody to translate your’s extension’s quotations. I strongly recommend to not spend too much effort on this, but to proceed to Version 6.2 (LTS) as soon as possible. I’ll do that, thus I won’t add any content for additional translations here.