Improve your PHP code testability
Tags: php, webdev, tutorial, programming
Published: 2021-09-19 16:12:49

## Introduction
![gif of a man welcoming you](https://media.giphy.com/media/l0MYC0LajbaPoEADu/giphy.gif?cid=ecf05e47mubvjwga5b9ynfv1d66s8dl7n27n0dwfagdngqvb&rid=giphy.gif&ct=g)
Welcome to my article! I hope you are like me and believe in the power of unit tests, or at least know that it's important to write them, right? In short words, I like them because it gives me more confidence that my code is working and no one else in the future will make changes there by mistake.

In this article I want to show you how to combine mocks with reflection and write a test that seemed impossible before.

Final code is on [Github](https://github.com/fabiothiroki/php-reflection-test).

 {% github fabiothiroki/php-reflection-test no-readme %} 

## Challenge accepted
![gif written challenge accepted](https://media.giphy.com/media/QxZ0nbcVgMlPlnfZos/giphy.gif?cid=ecf05e47mq4dg5zivdkvqglw6fu1m08j12nqyzx7s8g7lvop&rid=giphy.gif&ct=g)
I've just started my new job, in this company that uses PHP as a main language. Got a new ticket and I was excited to ship my new code (with tests, of course!).

But suddenly, a wild class appears! It doesn't expose one dependency that I needed to mock. I could refactor this code to receive this dependency by its constructor but decided to not follow this path because I was new (noob) at the company, so I didn't know if this was a common pattern, or what the original developer of this class was thinking when made this design.

The other alternative that I'm going to show here later was not supposed to be a big effort. Coming from a Java background, I knew this could be done using reflection.

## Reflection? What?
![gif written what](https://media.giphy.com/media/bm02BE6DQ4Oag8GXep/giphy.gif?cid=ecf05e47zfis7aps6etdsi1w7wwf6x1jorfzscywhjwte7k5&rid=giphy.gif&ct=g)

Before jumping into code, we need to understand the concept of reflection programming. Here goes my simple definition:

> Reflection is a tool that can be used to inspect your code and bypass static typing.

For example, in this article I will use reflection to change an instance of an unacessible attribute from a class that doesn't have a setter or another way to expose this attribute.

## Why would you want to do this?
![Class diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kstaqojvbeblfd66izs.png)

In this example, the `Implementation` class has a `public` method that calls a method from the unacessible dependency. So the test we want to write is this: is the dependency being called with correct arguments when `public` method from `Implementation`is called?

## Show me the code

### Abstract class
```php
<?php
abstract class AbstractClass
{
    protected $unacessibleDependency;

    public function __construct()
    {
        $this->unacessibleDependency = new UnacessibleDependency();
    }

    abstract function doSomething(string $argument): void;
}
```

As you can see, there's no common way we can access `$unacessibleDependency` from outside, unless we refactor this class, which I already said we don't want to follow this path for several reasons.

### Implementation class
```php
<?php
class ImplementationClass extends AbstractClass
{
    public function doSomething(string $argument): void
    {
        $this->unacessibleDependency->doSomethingElse($argument);
    }
}
```
`doSomething` is the method we want to test.

### Test setup
Start by creating an instance of the system under test (SUT):
```php
$this->implementation = new ImplementationClass();
```
Set the `unacessibleDependency` of the previous instance accessible by reflection:
```php
$reflectionClass = new ReflectionClass($this->implementation);
$property = $reflectionClass->getProperty('unacessibleDependency');
$property->setAccessible(true);
```
Create a mock instance of `UnacessibleDependency` so we can verify if it's being called:
```php
$this->mockUnacessible = $this->createMock(UnacessibleDependency::class);
```
Then substitute the original depedency by the mock one:
```php
$property->setValue($this->implementation, $this->mockUnacessible);
```
### Finally, the assertion
```php
$this->mockUnacessible->expects(self::once())
            ->method('doSomethingElse')
            ->with(self::equalTo('argument'));

$this->implementation->doSomething('argument');
```
Translating to human language: when `doSomething` is called with `argument` parameter, is `doSomethingElse` being called with the same parameter?

Now we have our test, and no one got hurt during this process.

![celebration gif](https://media.giphy.com/media/IwAZ6dvvvaTtdI8SD5/giphy.gif?cid=ecf05e47g4t1sip0uq1bck5ttucowfbi5zkzbhc9br08dhd0&rid=giphy.gif&ct=g)

## Conclusion
There's a way to add a unit test even in unlikely scenarios, but this technique should be used with caution, because we may be just adding an workaround to a poorly code design. If the code is not easily testable, it's probably not following the good practices of encapsulation and abstraction.

### I want to know more!
For more techniques to make your code more testable, check out the book ["Working Effectively with Legacy Code" by Michael C. Feathers](https://www.goodreads.com/book/show/44919.Working_Effectively_with_Legacy_Code).