How to unit test PHP traits

PhpUnit TestingPhpunitTraits

Php Problem Overview


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');
    }
}

Php Solutions


Solution 1 - Php

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());
    }
}
?>

Solution 2 - Php

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);
    }
}

Solution 3 - Php

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

$class = new class {
    use TheTraitToTest;
};

// We now have everything available to test using $class

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAliView Question on Stackoverflow
Solution 1 - PhpSchleisView Answer on Stackoverflow
Solution 2 - PhpvictorkurauchiView Answer on Stackoverflow
Solution 3 - PhpSteveView Answer on Stackoverflow