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.

Leave a Reply