# Testing Traits in PHPUnit
*5 techniques for testing traits with simple and complex use cases*

April 24, 2021 — by Doeke Norg

---

**Traits** are great. You can extract common logic into these mini "classes" and `use` them inside multiple classes to
share this logic. When you start testing these traits, things can get a bit bloated because you need to `use` a trait
on a class. You might find yourself creating a helper class only for the purpose of testing; a very common case.

Here are **5 alternative techniques** that might help reduce some of this bloatiness.

## 1. `use` the trait inside the test class

You have to `use` a trait inside a class. That's just how traits work. But instead of creating a class just for testing
purposes, why not use the class we already have? The test class itself!
Here is an example of what that might look like.

We start off with a simple trait that has a single method that returns a provided `boolean` state. *Yes that is a dumb
method; but it's only to get the point across.*

```php
<?php

namespace App\Mixin;

trait MyTrait {
    public function returnState(bool $state): bool {
        return $state;
    }
}
```

Now that we have the trait; we can `use` it inside the test class and perform some assertions.

```php
use App\Mixin\MyTrait;
use PHPUnit\Framework\TestCase;

class MyTraitTest extends TestCase {
    use MyTrait;
    
    public function testReturnState(): void {
        self::assertTrue($this->returnState(true));
        self::assertFalse($this->returnState(false));
    }
}
```

That was easy. No overhead of a custom class and fully tested! However, there might be two potential problems with this
approach:

1. The method name could be the same as one provided by the test class itself (naming collision).
2. There is no isolation; so the test class could actually influence the test results. For example when using a `static`
   variable inside the trait; that would be applied on the test class and can influence other test cases.

These drawbacks lead us to our second technique.

## 2. Scoping in an anonymous class

PHP 7 introduced us to anonymous classes. These classes can also *extend* other classes, *implement* interfaces and
*use* traits. While this technique still introduces a custom class, we do so "on the fly", and it is fully scoped to the
test case only; making it completely isolated from outside interference.

Building on top of our existing example; here is what that would look like.

```php
use App\Mixin\MyTrait;
use PHPUnit\Framework\TestCase;

class MyTraitTest extends TestCase {
    public function testReturnStateAnonymous(): void {
        $trait = new class {
            use MyTrait;
        };

        self::assertTrue($trait->returnState(true));
        self::assertFalse($trait->returnState(false));
    }
}
```

If only there was a way to reduce the anonymous class to a single line... a helper method perhaps?

## 3. Using a `getObjectForTrait` "mock"

As it turns out, PHPUnit has a method called `getObjectForTrait` that creates and returns an object that uses the
provided trait. This method can also receive arguments needed to create a class, in case your trait implements a
`__construct()` method that needs these arguments. This cleans up our test case a bit.

```php
use App\Mixin\MyTrait;
use PHPUnit\Framework\TestCase;

class MyTraitTest extends TestCase {
     public function testReturnStateObject(): void {
        $trait = $this->getObjectForTrait(MyTrait::class);

        self::assertTrue($trait->returnState(true));
        self::assertFalse($trait->returnState(false));
    }
}
```

It doesn't get much simpler than this; but our use case is also very simple. What if it was more complex, with calls to
other methods that are not on the trait, or methods that should only work when a class implements a certain interface?

## 4. Using a `MockForTrait` mock object

Let's update our trait to include a method that references a method not on the trait.

```php
trait MyTrait {
    // ...
    public function callUnavailable(bool $state): bool {
        return $this->unavailableMethod($state);
    }
}
```

Looking back at the previous techniques, we could:

1. `Use` it on the test class and implement `unavailableMethod` there too
2. Create an anonymous class that implements the `unavailableMethod`

However, what if we would rather mock that method to be able to assert the times the method was called, and with what
parameters?

We can use the `getMockForTrait` method provided by PHPUnit. This helper method creates a mock object that uses the
provided trait. We can provide an array of methods we would like to be mocked. This gives us way more testing
possibilities.

```php
use PHPUnit\Framework\TestCase;

class MyTraitTest extends TestCase {
    public function testCallUnavailable(): void {
        $trait = $this->getMockForTrait(MyTrait::class, [], '', true, true, true, [
            'unavailableMethod',
        ]);
        
        // Here we can assert the times the method was called, and with what parameters.
        $trait
            ->expects(self::exactly(2))
            ->method('unavailableMethod')
            ->withConsecutive([true], [false])
            ->willReturnArgument(0); // still return the provided state

        self::assertTrue($trait->callUnavailable(true));
        self::assertFalse($trait->callUnavailable(false));
    }
}
```

Now there is still the use case of implementing an `interface`.

## 5. Using an `abstract` helper class

Sometimes you'll have a trait that is only supposed to be used for classes that implement a certain `interface`. So lets
say we have a complex interface called `App\Contract\ComplexInterface` which has a bunch of functions that have no part
in the trait. Looking at our earlier techniques we can only use a (anonymous) helper class that implements that
interface, and uses the trait. This is quite annoying as we now need to at least implement al these methods. Not ideal.

**Fun fact**: Did you know you can implement an interface on a class without actually adding any of the methods? PHP
doesn't mind at all, if you mark your class as `abstract`. PHP figures you will implement these methods on the concrete
class that extends from this class. It actually marks these methods as `abstract` themselves! This is great news for us
as we can now use the `getMockForAbstractClass()` method from the PHPUnit test class.

The `getMockForAbstractClass()` creates a mock object, but only mocks the abstract methods. It keeps concrete methods
intact. So in our use case every method from the interface will be mocked, but the methods from the trait will not!

We will implement an `isComplex()` method on the trait that checks if the class is an instance of `ComplexInterface`.

```php
use App\Contract\ComplexInterface;

trait MyTrait
{
    public function isComplex(): bool {
        return $this instanceof ComplexInterface;
    }
}
```

Now we can create an `abstract` helper class that implement the interface and uses our trait.

```php
use App\Contract\ComplexInterface;

abstract class HelperClass implements ComplexInterface {
    use MyTrait;
}
```

At this point we can use `getMockForAbstractClass()` to create a mock object based on that class and perform our
assertions. And to prove our use case actually works, we also use technique 3 to show that will return `false`.

```php
class MyTraitTest extends TestCase {
    public function testIsComplex(): void {
        $trait_mock = $this->getMockForAbstractClass(HelperClass::class);
        $trait_object = $this->getObjectForTrait(MyTrait::class);

        self::assertTrue($trait_mock->isComplex());
        self::assertFalse($trait_object->isComplex());
    }
}
```

## Bonus: easily test `protected` methods on a trait

Testing `protected` methods usually implies you either test them through another method that calls these methods, or you
extend the class and update their visibility to `public` by overwriting them and calling the parent. Traits, however,
have a special trick up their sleeve: you can update their visibility while `use`-ing them. Here's how that works:

```php
trait MyTrait {
    protected function myProtectedMethod(): bool {
        return true;
    }
}

class MyTraitTest extends TestCase {
    public function testMyProtectedMethod(): void {
        $trait = new class {
            use MyTrait {
                myProtectedMethod as public; // make the method public
            }
        };

        // We can now simply call the method, as it is now public in this test class.
        self::assertTrue($trait->myProtectedMethod());
    }
}
```

So in conclusion; traits are still great, and testing them is actually quite easy; even in complex situations. Did you
learn any new techniques or think someone else needs to learn them? Then please share this post.
