Доступ к защищенным свойствам через общедоступный метод

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

function() setUp() 
{
    $this->shipping_method = $this->getMockBuilder(Wc_Trincargo_Shipping_Method::class)
                    ->getMock();

    $this->shipping_method->set_post_data([
        'woocommerce_wc-trinicargo-shipping_waybill_password' => 'xxx',
        'woocommerce_wc-trinicargo-shipping_waybill_username' => 'xxxx',
        'woocommerce_wc-trinicargo-shipping_waybill_customer_id' => uniqid(),
        'woocommerce_wc-trinicargo-shipping_waybill_pickupdays' => 2
    ]);
}

set_post_data — это общедоступный метод, который задает защищенное свойство.

Позже я тестирую вызов другого метода, который должен проверить указанное свойство protected. Я знаю, что они говорят, что вы не можете издеваться над защищенными и частными свойствами, но если свойства устанавливаются общедоступными методами... разве это не должно работать?


person KArneaud    schedule 17.10.2017    source источник
comment
Вы не должны знать, что метод устанавливает защищенные свойства в первую очередь. Это как-то неправильно. Это внутренняя реализация метода, и вы не должны знать об этом при тестировании.   -  person Royal Bg    schedule 17.10.2017
comment
@RoyalBg не уверен, что вы пытаетесь сказать здесь.   -  person KArneaud    schedule 17.10.2017
comment
Я пытаюсь сказать, что вы тестируете методы на соответствие бизнес-спецификации, а не на знание лежащего в их основе кода. Это не правильно. Представьте, что вы тестируете скомпилированную библиотеку и не знаете внутреннего поведения кода, вы знаете только то, как он ведет себя, продиктованное бизнесом.   -  person Royal Bg    schedule 17.10.2017
comment
изначально я тестировал логику указанного метода, однако тест не прошел из-за того, что указанное свойство не было установлено, что на самом деле не было частью теста, если это имеет какой-либо смысл   -  person KArneaud    schedule 17.10.2017
comment
Вам нужно изолировать зависимости, которые есть у этого метода, чтобы запустить настоящий unit test. Ваш тест не пройден, потому что некоторые другие зависимости не работают должным образом. Тестируйте их отдельно и издевайтесь над ними здесь. Если вы не можете изолировать зависимости, скорее всего, архитектура неверна.   -  person Royal Bg    schedule 17.10.2017
comment
Я тестирую расширения woo commerce. Даже не уверен, насколько это возможно, что мне нужно иметь его, чтобы иметь правильную среду для тестирования.   -  person KArneaud    schedule 17.10.2017
comment
о, это совсем другое. Проверьте, есть ли специализированный фреймворк для тестирования woocommerce. У них самих в проекте есть тесты — проверьте, как они это делают.   -  person Royal Bg    schedule 17.10.2017


Ответы (2)


Если вам действительно нужно получить доступ к защищенному свойству в тесте и у вас нет геттера (и вы не должны создавать его исключительно для теста), вы можете использовать отражение.

<?php

class MyClass
{
    protected $myProperty;

    public function setMyProperty($value) 
    {
        $this->myProperty = $value;
    }
}


$a = new MyClass();
$a->setMyProperty('TestValue');

// echo $a->myProperty; Can't do this because it's protected.

$r = new ReflectionProperty('MyClass', 'myProperty');
$r->setAccessible(true);
$value = $r->getValue($a);

echo $value; // 'TestValue'
person Mikey    schedule 17.10.2017

Неважно, что свойство protected было получено из общедоступного метода. Видимость принадлежит свойству в любом случае. Если вам нужно впоследствии проверить значение свойства, создайте общедоступный геттер для этого свойства.

Обратите внимание, что бесполезно тестировать простые геттеры и сеттеры. Вместо этого вы должны проверить, что класс делает со значениями свойств.

Пример бесполезного теста:

class MyClass {
    private $prop;
    public function setProp($value) {
        $this->prop = $value;
    }
    public function getProp() {
        return $this->prop;
    }
}

Контрольная работа

$myClass = new MyClass();
$myClass->setProp('foo');
assertTrue($myClass->getProp() === 'foo');

Пример класса, который использует реквизит осмысленным образом, который определяет поведение/вывод другого метода:

class MyClass2 {
    private $prop;
    public function setProp($value) {
        $this->prop = $value;
    }
    public function getPropUpperCase() {
        return strtoupper($this->prop);
    }
}

Контрольная работа

$myClass2 = new MyClass();
$myClass2->setProp('foo');
assertTrue($myClass->getPropUpperCase() === 'FOO');
person OptimusCrime    schedule 17.10.2017
comment
Изменение определения класса для удовлетворения потребностей теста кажется мне неправильным. Если для этого свойства не было getX() и оно было установлено как protected, скорее всего, это было сделано по какой-то причине. Добавление дополнительного метода только для того, чтобы он мог получить к нему доступ в тесте, на самом деле не рекомендуется. Если нет причины, то, очевидно, просто добавьте геттер, это самое простое решение. - person Mikey; 17.10.2017
comment
@Pigeon Предпосылка теста ошибочна в любом случае. Какая необходимость проверять значение свойства, если оно защищено после того, как оно было установлено публичным сеттером? Тесты выложены все неправильно. - person OptimusCrime; 17.10.2017
comment
Если вы согласны с идеей TDD, вам нужно написать тесты для реализации средств доступа и мутаторов. Если вы не разделяете идею TDD, то написание тестов для аксессоров и мутаторов может доказать отсутствие дефектов. - person localheinz; 17.10.2017
comment
@localheinz В каком-то странном пограничном случае это может иметь место, но в противном случае вы могли бы сделать это поле неизменяемым и разрешить устанавливать его только один раз. Когда вы проверяете свою фактическую функциональность, выходные данные этой функции будут отражать значение свойства. Классы рефлектора обычно избегают, если только это не последний вариант. Моя точка зрения заключалась в том, что проверка значения (скрытого) свойства сама по себе бесполезна. Это свойство используется для и должно быть протестировано. Подписка на TDD не означает создание модульных тестов или макетов для каждого отдельного свойства в классе. - person OptimusCrime; 17.10.2017
comment
@OptimusCrime Я не тестирую свойства get или set. На самом деле я тестирую метод, который делает что-то еще, но проверяет и извлекает защищенное свойство в процессе, который жизненно важен для выполнения остальной логики. - person KArneaud; 17.10.2017
comment
если он извлекает свойство, проверьте результат метода, затем - person Royal Bg; 17.10.2017
comment
@Kendall Ваш вопрос здесь кажется проблемой x вместо y. Вы можете использовать классы-рефлекторы, чтобы делать то, что хотите, или указать свою реальную проблему, потому что кажется, что ваша архитектура имеет некоторые недостатки. - person OptimusCrime; 17.10.2017
comment
@OptimusCrime моя проблема в том, что мне нужно было установить защищенное свойство, чтобы использовать рассматриваемый метод, чтобы я мог проверить результаты метода. Из-за этого результаты метода терпят неудачу, что выходит за рамки начального условия тестирования. - person KArneaud; 17.10.2017