Результат тестирования и макета вложенного контейнера

Недавно я взял на себя проект PHP, который содержит мало или совсем не тестирует. Этот проект довольно большой, классы в основном довольно маленькие, но есть одна большая проблема.

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

В прошлый четверг я создал новую команду из 3 человек, чья новая обязанность — сосредоточиться исключительно на тестировании и его автоматизации, и сегодня, наконец, один из них пришел ко мне с вопросом, которого я боялся. Вопрос, на который я не знаю ответа.

Дэвид, как я могу посмеяться над результатом метода, который
скрыто глубоко внутри DI?

Переписывать модули для следования DI недопустимо, у нас нет ни бюджета, ни времени для этого.

do() метод можно назвать так?

class Baz extends AbstractBaz
{
public function foo()
{
$userProcess = $this->DI->Foo->Bar->FooBar->BarFoo->getUserBar();
$users = $userProcess->do();

// work with the $users variable
}
}

Сам локатор огромен, вы можете вызывать сотни методов, погружаясь все глубже и глубже.

Есть ли способ быстро высмеять Foo->Bar->FooBar->BarFoo->getUserBar результат? Переменные доступны через магию __get метод и намекнул @property аннотаций.

С помощью PHPUnit, было бы неплохо иметь что-то вроде этого:

$locator = $this
->getMockBuilder('\App\DI\Abstracted\DI')
->setMethods(['Foo->Bar->FooBar->BarFoo->getUserBar'])
->getMock();

$locator
->expects($this->any())
->method('Foo->Bar->FooBar->BarFoo->getUserBar')
->will($this->returnValue($desiredObject));

К сожалению, это на самом деле не работает. Я не очень опытный с PHPUnit сам. Есть ли обходной путь, который я еще не нашел?

1

Решение

Как я уже упоминал в моих комментариях, вы мог заглушить контейнер, который возвращает себя всякий раз, когда __get Метод вызывается и возвращает макет объекта из фактического метода, который вы вызываете в конце. использованияКонтактная codeception-х Stub составная часть, это может выглядеть примерно так:

$container = Stub::make(
'Your\Container',
[
'getUserBar' => $returnMock,
]
);
Stub::update($container, ['__get' => $container]);//return itself on __get access

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

1

Другие решения

Я нашел решение для PHPUnit ранее, но только что нашел время, чтобы ответить.

Имитация того, что я хотел на самом деле возможно в PHPUnit, используя PHP Closure, это то, что анонимные функции в PHP называются.

Вот небольшой рабочий пример PHPUnit

$classA = $this
->getMockBuilder('First\Class\To\Be\Mocked')
->disableOriginalConstructor()
->setMethods([
'The',
'names',
'of',
'desired',
'methods',
])
->getMock();

$classB = $this
->getMockBuilder('Second\Class\To\Be\Mocked')
->disableOriginalConstructor()
->setMethods([
'The',
'names',
'of',
'desired',
'methods',
])
->getMock();

$serviceLocator = $this
->getMockBuilder('Second\Class\To\Be\Mocked')
->disableOriginalConstructor()
->setMethods([
'__get',
'SomeMethod',
])
->getMock();

$serviceLocator
->expects($this->any())
->method('__get')
->will($this->returnCallback(function($name) use ($serviceLocator, $classA, $classB)
{
switch ($name)
{
case 'ClassA':
return $classA;
case 'ClassB':
return $classB;
default:
return $serviceLocator;
}
}));

Этот код при выполнении даст желаемые результаты.

$serviceLocator->This->That->Them->ClassA вернет определенный $classA переменная, изменение ClassA на ClassB заставляет сервисный локатор возвращать $classB переменная вместо

Используя обратные вызовы, вы можете делать что угодно, даже имитировать возвращаемые значения через ссылочные параметры.

0

По вопросам рекламы [email protected]