Use Anonymous Classes to Test Traits

I’m guilty of creating stub-like classes in my tests to unit test traits, sometimes. So, you end up with a special class inside your unit test file, perhaps at the bottom, that is empty but only extends the trait or something like that. This is not a good idea, but it was my only way that I could figure out how to unit-test traits separately - especially if they were made of protected methods.

But there is a better way: use anonymous classes to test your trait.

Let’s look at an example. Here we’ll look at our trait. Our trait basically just filters a string to lowercase.

src/Traits/FilterToLCTrait.php

<?php
namespace App\Traits;

trait FilterToLCTrait
{
  public function filterStringToLC($string)
  {
    return strtolower($string);
  }
}

And now, let’s take a look at one of our tests in our test case.

$class = new class { use FilterToLCTrait; };
$this->assertEquals('abc', $class->filterStringToLC('AbC'));

This way we can test our trait without having to make our stub class somewhere in this file.

The only trouble you might have is if your trait has a protected method. I tend to just create a ‘proxy’ method to enable that. (You could use reflection to make it public for your test, I’m sure, too. There are many ways to solve a single problem.)

Let’s imagine now that the signature for the method has changed from public to protected - and we still want to test this smaller unit.

$class = new class { 
  use FilterToLCTrait; 
  public function proxyFilterStringToLC()
  {
    return call_user_func_array([$this, 'filterStringToLC'], func_get_args());
  };
};
$this->assertEquals('abc', $class->proxyFilterStringToLC('AbC'));

Here, we just proxy the call to our protected method, passing in whatever would be incoming functions to the new method. I wrote it with func_get_args and call_user_func_array because I don’t want to have to edit all of these internal implementations if I change my function’s signature or parameter list.

There are arguments that you should never be unit testing something like a trait with a protected method. Instead, you should only be testing the class that implement that trait. I think that’s fine, for the most part, but sometimes your trait is pretty complex. And then, you’d have to test each scenario individually on each class - if you were following that line of logic. Instead, I tend to test the trait like this with all the ways I can think of - then when it comes to my implementing class, I can focus on testing it’s own interior logic, knowing that I only need to have one successful path through the trait to verify it is in-fact implemented. I think this is “not the right way” - but sometimes solutions outside of the ‘way the book says to do it’ make more sense in real life, at least to me.

php, phpunit, testing

Return to All Posts