M
M
MasterCopipaster2020-08-17 11:34:22
PHPUnit
MasterCopipaster, 2020-08-17 11:34:22

How to lock private property in phpunit class?

Help the people, even caught a dumbass, in general, there is a beginning of such a class

class Relay implements ConsumerInterface
{
    /**
     * @var MemcachedAdapter
     */
    private $memory;

    public function __construct()
    {
        $client = MemcachedAdapter::createConnection('memcached://localhost');
        $this->memory = new MemcachedAdapter($client, $namespace = '',$defaultLifetime = 0);

I need to write a test for it, for this I need to lock $this->memory
I know how to write a mock, but I don’t know how to replace it in a private property.
In short, how to replace $this->memory so that my mock is there?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Maxim Fedorov, 2020-08-17
@MasterCopipaster

Simulation of your object:

class Relay
{
    private $privateProperty;

    public function __construct()
    {
        $this->privateProperty = new \stdClass();
    }

    // метод, который работает с приватным свойством
    public function call()
    {
        return $this->privateProperty;
    }
}

Method 1: Native PhpUnit + Reflection API
use PHPUnit\Framework\TestCase;

class RelayTest extends TestCase
{
    public function testCall(): void
    {
        $reflectionClass = new \ReflectionClass(Relay::class);
        $reflectionProperty = $reflectionClass->getProperty('privateProperty');
        $reflectionProperty->setAccessible(true);

        // создаем наш объект БЕЗ конструктора
        $relay = $reflectionClass->newInstanceWithoutConstructor();

        // Меняем свойство и вызываем метод, работающий с этим приватным полем
        $reflectionProperty->setValue($relay, 1111);
        self::assertEquals(1111, $relay->call());

        // Меняем свойство и вызываем метод, работающий с этим приватным полем
        $reflectionProperty->setValue($relay, 'aaaa');
        self::assertEquals('aaaa', $relay->call());
    }
}

Method 2: Via Codeception Stub
class RelayTest extends TestCase
    public function testCall(): void
    {
        /** @var Example $stub */
        $stub = Stub::make(Relay::class, [
            'privateProperty' => 1111,
        ]);
        self::assertEquals(1111, $stub->call());

        $stub = Stub::make(Relay::class, [
            'privateProperty' => 'aaaa',
        ]);
        self::assertEquals('aaaa', $stub->call());
    }
}

Digressions and useful tips:
  • For some reason, the standard, long-used by me, method with the injection of a private field mock did not work and always returned null.
  • Use the Inversion of Control Principle, such as constructor dependency injection or assignment method injection in the NullObject constructor. Both ways will be good for testing.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question