Макет PHPUnit — вызов родительского __get/__set/__isset

Я пытаюсь издеваться над классом, который унаследовал магические методы, но они не реализуются, и я понятия не имею, как это исправить. Вот код:

<?php
use PHPUnit\Framework\TestCase;

class AnnoyingTestCase extends TestCase
{
    public function testNoAttributes()
    {
        $mock = $this->createMock(Child::class);
        $mock->attribute = 'value';

        $this->assertEquals($mock->attribute, null);
    }
}

class Base
{
    private $attributes = [];

    public function __set($name, $value)
    {
        $this->attributes[$name] = $value;
    }

    public function __get($name)
    {
        return $this->attributes[$name] ?? null;
    }

    public function __isset($name)
    {
        return isset($this->attributes[$name]);
    }
}

class Child extends Base
{
}

Я ожидал бы, что $mock->attribute будет иметь значение, которое я ему присвоил, но это не так, потому что __set не вызывается.

Если я попытаюсь использовать ->addMethods(['__get', '__set']), то выдаст исключение: Trying to set mock method "__get" with addMethods(), but it exists in class "Child". Use onlyMethods() for methods that exist in the class.. И все же добавление var_dump внутри метода __set показывает, что он не вызывается.

Что я могу сделать, чтобы у него были эти методы? Обратите внимание, что я не собираюсь реализовывать свой собственный эквивалент этих методов, я хочу, чтобы макет имел эти оригинальные методы.


person jurchiks    schedule 25.09.2019    source источник
comment
Весь смысл макета в том, что он не использует какое-либо существующее поведение класса, но оставляет все функции пустыми, чтобы вы могли решить, что они будут возвращать в конкретных случаях. Что вы пытаетесь проверить в этом тесте? Если вы хотите протестировать магические методы дочернего класса, вам следует создать фактический экземпляр этого класса и впоследствии утверждать, что класс имеет ожидаемое значение. Прямо сейчас кажется, что вы тестируете макет, что не имеет особого смысла.   -  person Dirk Scholten    schedule 26.09.2019
comment
@DirkScholten Я тестирую код, который использует класс, эквивалентный классу Child выше, и хотел избежать написания шаблонного кода.   -  person jurchiks    schedule 30.09.2019


Ответы (1)


Найдено исправление именно для этой проблемы, но оно может принести больше вреда, чем пользы. Работает следующий код:

$mock = $this->getMockBuilder(Child::class)
    ->enableProxyingToOriginalMethods()
    ->getMock();

Однако существует проблема, заключающаяся в том, что это проксирует ВСЕ исходные вызовы методов, и нет возможности сузить методы, которые вы хотите проксировать. И самый большой - потенциально неработающие вызовы конструктора - описаны здесь: PHPUnit - MockBuilder::enableProxyingToOriginalMethods() прерывается, когда исходный конструктор вызывает общедоступный метод

person jurchiks    schedule 25.09.2019