66

I want to know if there is a solution on how to unit-test a PHP trait.

I know we can test a class which is using the trait, but I was wondering if there are better approaches.

Thanks for any advice in advance :)

EDIT

One alternative is to use the Trait in the test class itself as I'm going to demonstrate bellow.

But I'm not that keen on this approach since there is no guaranty there are no similar method names between the trait, the class and also the PHPUnit_Framework_TestCase (in this example):

Here is an example trait:

trait IndexableTrait
{
    /** @var int */
    private $index;

    /**
     * @param $index
     * @return $this
     * @throw \InvalidArgumentException
     */
    public function setIndex($index)
    {
        if (false === filter_var($index, FILTER_VALIDATE_INT)) {
            throw new \InvalidArgumentException('$index must be integer.');
        }

        $this->index = $index;

        return $this;
    }

    /**
     * @return int|null
     */
    public function getIndex()
    {
        return $this->index;
    }
}

and its test:

class TheAboveTraitTest extends \PHPUnit_Framework_TestCase
{
    use TheAboveTrait;

    public function test_indexSetterAndGetter()
    {
        $this->setIndex(123);
        $this->assertEquals(123, $this->getIndex());
    }

    public function test_indexIntValidation()
    {
        $this->setExpectedException(\Exception::class, '$index must be integer.');
        $this->setIndex('bad index');
    }
}
cezar
  • 11,616
  • 6
  • 48
  • 84
Ali
  • 2,993
  • 3
  • 19
  • 42
  • Please provide code you've tried to do this with which does not work. That'll help someone assist you. – Adam B Jun 26 '15 at 23:00
  • @AdamB, I wrote the [first answer](http://stackoverflow.com/a/31083193/4960774) myself, which has a sample code. but please note its not like something is broken or not working, i just want to know if there are any good methods to unit test traits directly and not indirectly by unit testing a class which uses that trait. tanks – Ali Jun 26 '15 at 23:18

3 Answers3

109

You can test a Trait using a similar to testing an Abstract Class' concrete methods.

PHPUnit has a method getMockForTrait which will return an object that uses the trait. Then you can test the traits functions.

Here is the example from the documentation:

<?php
trait AbstractTrait
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class TraitClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $mock = $this->getMockForTrait('AbstractTrait');

        $mock->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($mock->concreteMethod());
    }
}
?>
powtac
  • 40,542
  • 28
  • 115
  • 170
Schleis
  • 41,516
  • 7
  • 68
  • 87
13

Since PHP 7 we can now use annonymous classes...

$class = new class {
    use TheTraitToTest;
};

// We now have everything available to test using $class
Steve
  • 1,853
  • 16
  • 30
11

You can also use getObjectForTrait , then assert the actual result if you want.

class YourTraitTest extends TestCase
{
    public function testGetQueueConfigFactoryWillCreateConfig()
    {
        $obj = $this->getObjectForTrait(YourTrait::class);

        $config = $obj->getQueueConfigFactory();

        $this->assertInstanceOf(QueueConfigFactory::class, $config);
    }

    public function testGetQueueServiceWithoutInstanceWillCreateConfig()
    {
        $obj = $this->getObjectForTrait(YourTrait::class);

        $service = $obj->getQueueService();

        $this->assertInstanceOf(QueueService::class, $service);
    }
}
victorkurauchi
  • 1,390
  • 18
  • 23