Как вы пишите тестируемый код?

Суть вопроса: Если не опираться не на какой фреймворк а взять за основу простую реализацию MVC, без dependency injection.
Например нужно протестировать метод prepareBuy в модели User,
но при тесте метода видно что внутри вызывается метод order->paid($userId),
который обращается к внешнему источнику, который не нельзя дергать. соответственно при тестировании необходимо обернуть его в Mock:

class MockTest extends PHPUnit_Framework_TestCase
{
    public function testUserPay()
    {
        $order = $this->getMock('Order', ['paid']);

        $order->expects($this->once())
            ->method('paid')
            ->will($this->returnValue('ok'));

        $subject = new User;
        $subject->attach($order);

        $this->assertEquals('ok', $subject->prepareBuy(999));
    }


class User
{
    protected $order = null;

    public function attach(Order $order)
    {
        $this->order = $order;
    }

    public function prepareBuy($userId)
    {
        return $this->order->paid($userId);

    }
}

class Order
{
    public function paid($argument)
    {
        return $argument;
    }

}


Собственно вопрос: Как лучше писать код для таких тестов ?
Писать в каждой модели конструктор? в который передавать экземпляры классов, а если источников будет много: придется в коде вызывать например:
$user = new User()->attach(new Order);
$user->prepareBuy(999);

что не очень удобно.
Передавать объект в параметр метода?
Например если через позднее статическое связывание чтобы, не делать new
User::model()->prepareBuy(new Order, New Profile, $userId)

но если надо вызывать метод много раз, становиться неудобно, писать параметры заново.
Хочется услышать совета, как вы решаете данную проблему ?
  • Вопрос задан
  • 1271 просмотр
Пригласить эксперта
Ответы на вопрос 3
alexey-m-ukolov
@alexey-m-ukolov Куратор тега PHP
Для решения этой проблемы и придумали Dependency Injection и IoC-контейнер.
Ответ написан
Комментировать
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
соответственно при тестировании необходимо обернуть его в Mock

Ну как бы это логично, при юнит тестировании все, кроме того что мы непосредственно тестим, должно быть заменено моком/стабом.

Писать в каждой модели конструктор?

Что вы понимаете под "модель"? Ваши бизнес-объекты? У них в конструкторе должно быть только то, что им нужно.

а если источников будет много

То это повод пересмотреть архитектуру, почитать про цепочки обязанностей, стратегии, про сегрегацию интерфейсов в конце концов.

User::model()->prepareBuy(new Order, New Profile, $userId)


Прочитайте эту строчку кода, и скажите что тут происходит, ибо я не могу этого понять.

Ну и да, помимо внедрения зависимостей в конструктор, есть еще такая неплохая штука как double dispatch, когда нужные сервисы передаются как аргументы методов, которым они нужны. Так наш класс не зависит от непонятных вещей и таким образом мы все можем спокойно тестить.
Ответ написан
@matperez
Почитайте книжулю The Clean Architecture in PHP. Там и про архитектуру и про тесты есть.
Ответ написан
Ваш ответ на вопрос

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

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