stanislav-belichenko
@stanislav-belichenko
Backend PHP Developer

PHPUnit: какой right way для тестирования классов, для которых требуется протестировать и их начальное создание/заполнение?

Например, у нас есть класс, реализующий интерфейс `ArrayAccess`. Соответственно, даже просто наполняя его значениями, мы уже используем его функционал, который тоже должен быть протестирован, а именно метод offsetSet(). То есть у нас есть тест для собственно метода OffsetSet():

protected function setUp()
    {
        $this->Filter = new Filter();
    }

public function testOffsetSet()
    {
        $this->Filter[ 'page' ]        = 1;
        $this->Filter[ 'limit' ]       = 2;
        $this->Filter[ 'category_id' ] = 3;

        $this->AssertEquals($this->Filter->get_array(), [
            'page'        => 1,
            'limit'       => 2,
            'category_id' => 3,
        ]);
    }


И тут мы проверяем, будет ли работать конструкция с квадратными скобками. Метод get_array() вспомогательный, его сейчас не трогаем, он просто возвращает нам массив, который мы храним в этом объекте.

Потом мы начинаем тестировать следующий функционал, например метод offsetGet:

public function testOffsetGet()
    {
        $this->Filter[ 'page' ]        = 1;
        $this->Filter[ 'limit' ]       = 2;
        $this->Filter[ 'category_id' ] = 3;

        echo $this->Filter[ 'page' ];
    }


И в этот момент становится понятно, что мы в этом методе используем функционал, тестируемый в другом, а именно присвоение через квадратные скобки, которое на самом деле у нас происходит с вызовом OffsetSet(). В целом ничто не мешает мне вынести это присвоение в setUp() и там его делать каждый раз, ну или сочинить некий dataProvider, но нет ли более верного пути для всего этого? Как тогда поступить, как разнести по тестам (и связать их сами) создание тестируемого класса, его наполнение первоначальными данными, проверку наполнения его первоначальными данными и другие тесты?

Про @depends и даже про @depends clone я слышал, первое я успешно на данный момент использую вот так:

private $Filter;

protected function setUp()
{
    $this->Filter = new Filter();
}

protected function tearDown()
{
    unset($this->Filter);
}

/**
 * @return Filter
 */
public function testOffsetSet()
{
    $this->Filter[ 'page' ]        = 1;
    $this->Filter[ 'limit' ]       = 2;
    $this->Filter[ 'category_id' ] = 3;

    return $this->Filter;
}

/**
 * @depends testOffsetSet
 *
 * @param Filter $filter
 */
public function testGet_array($filter)
{
    $this->AssertEquals($filter->get_array(), [
        'page'        => 1,
        'limit'       => 2,
        'category_id' => 3,
    ]);
}


Вопросы:

Верен ли такой подход?
Не лучше ли первоначальное присвоение делать на стадии setUp()?
И главный вопрос - как использовать @depends clone? Я не очень понял логики этого способа, да и вообще семантики, при которой он заработает.
  • Вопрос задан
  • 52 просмотра
Пригласить эксперта
Ответы на вопрос 1
@smple
ArrayAccess это interface и соответственно надо не его тестировать а конкретные методы класса и проверять что класс реализует его.

для примера пусть есть класс Magic который реализует ArrayAccess то MagicTest может быть примерно вот так

class MagicTest extends Test
{
   public function __setUp()
   {
       $this->magic = new Magic();
   }
   public function providerValue()
   {
     return [['key', 123],['key', 'string']];// и тд
   }
   /**
    * @dataProvider providerValue
    * @depends testMustBeArrayAccesable
    */
   public function testSet($key, $value)
   {
        $this->magic[$key] = $value;
        assertSame($value, $this->magic[$key]);
   }

   public fuinction testMustBeArrayAccesable()
   {
        assertTrue($this->magic instanceof ArrayAccess);
   }
}


но смысла нет писать тест на ArrayAccess надо проверять то что требуется от класса а не от его интерфейса.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы