TDD Getter Behaviour
Lucky you! You found an early release chapter - it will be fully polished and published shortly!
This Chapter isn't quite ready...
Rest assured, the gnomes are hard at work
completing this video!
We have our first test for TranslatedObject
, which is passing. When we
wrote that test, we didn't truly use Test Driven Development, because
the logic already existed - we just confirmed it with a test.
True TDD is when you write a test for something that doesn't yet exist, in a way you want it to work, see it fail, then write the code to make it pass. So, let's do that now!
Another Test
We want TranslatedObject::__call()
to first check if there's a getter
available. When calling title()
, if this method doesn't exist on the
inner object, try and call getTitle()
.
In TranslatedObjectTest
, below our first test, create a new one
public function testCallUsesGetterIfAvailable()
:
// ... lines 1 - 7 | |
class TranslatedObjectTest extends TestCase | |
{ | |
// ... lines 10 - 20 | |
public function testCallUsesGetterIfAvailable() | |
{ | |
// ... lines 23 - 25 | |
} | |
} | |
// ... lines 28 - 45 |
Inside, our setup
will be the same so copy the $object =
from the test above and paste
it here:
// ... lines 1 - 7 | |
class TranslatedObjectTest extends TestCase | |
{ | |
// ... lines 10 - 20 | |
public function testCallUsesGetterIfAvailable() | |
{ | |
$object = new TranslatedObject(new ObjectForTranslationStub()); | |
// ... lines 24 - 25 | |
} | |
} | |
// ... lines 28 - 45 |
Now for the assertion. In our stub object, we have a prop3
property with
a getter: getProp3()
. So, if calling the prop3()
method on our wrapper
(without the get
) we want to forward this call to getProp3()
on the inner object.
$this->assertSame('value3', $object->prop3())
- that's it!
// ... lines 1 - 7 | |
class TranslatedObjectTest extends TestCase | |
{ | |
// ... lines 10 - 20 | |
public function testCallUsesGetterIfAvailable() | |
{ | |
// ... lines 23 - 24 | |
$this->assertSame('value3', $object->prop3()); | |
} | |
} | |
// ... lines 28 - 45 |
At your terminal, run the tests with:
symfony php vendor/bin/phpunit object-translation-bundle/tests
This test fails - but that's what we expect! This basically matches the error we're seeing from Twig. Now we need to write the code to make it pass.
Implementing the Logic
In TranslatedObject::__call()
, at the beginning of the method, set
$method = $name
. Now, add a check: if (!method_exists($this->_inner, $name))
.
Inside, write $method = 'get'.ucfirst($name)
- this capitalizes the first
letter of the name and prepends get
:
// ... lines 1 - 9 | |
final class TranslatedObject | |
{ | |
// ... lines 12 - 19 | |
public function __call(string $name, array $arguments): mixed | |
{ | |
$method = $name; | |
if (!method_exists($this->_inner, $method)) { | |
$method = 'get'.ucfirst($name); | |
} | |
// ... lines 27 - 28 | |
} | |
// ... lines 30 - 39 | |
} |
Below, in the return
, change $name
to $method
. That should be it!
// ... lines 1 - 9 | |
final class TranslatedObject | |
{ | |
// ... lines 12 - 19 | |
public function __call(string $name, array $arguments): mixed | |
{ | |
// ... lines 22 - 27 | |
return $this->_inner->$method(...$arguments); | |
} | |
// ... lines 30 - 39 | |
} |
Verifying the Behavior
Back in your terminal, run the tests again:
symfony php vendor/bin/phpunit object-translation-bundle/tests
Green! TDDS - Test Driven Development Success!
Note that both our tests pass - that's important as it means we didn't break any existing functionality.
Now for the true test - let's see if this fixes our Twig issue.
In your browser, on the article page that has the error, refresh... and perfect! Error's gone. The title and content are properly being pulled from the underlying object.
There's likely more edge cases we need to consider, but this is a great start. Now that we have this test, if we do find an edge case, we can add a test for it, see it fail, then implement the logic to make it pass. TDD Goodness!
Next, let's again use TDD to bang out a feature - the actual object translations!