Концепция Mocking была построена с мыслью, что вы будете использовать внедрение зависимостей. Я, конечно, могу понять, почему вы можете не захотеть использовать внедрение зависимостей с таким множественным наследованием, как модель, которую использует php под названием «Черты». Инструменты-насмешки, подобные тем, что были созданы для phpunit, были созданы для замены экземпляров объектов, а не самих классов / интерфейсов / признаков. PHP Traits больше похожи на статическую зависимость, а не зависимость от экземпляра объекта. Однако, даже если вы использовали черты и предполагали, что черта в основном совпадает с классом, в соответствии с насмешливыми рекомендациями следует проверять черту как ее собственный тест, а не проверять черту через другой класс. Если вы хотите смоделировать саму черту, вы можете попытаться пересмотреть свой дизайн, поскольку я не верю, что это можно сделать. Вы, конечно, можете смоделировать черту и протестировать эту черту, но вы не можете смоделировать черту, а затем внедрить ее как зависимость от объекта. Представьте себе, что класс, например, реализует интерфейс, насмешка над чертой была бы такой же, как насмешка над интерфейсом, которую реализует класс, это невозможно. Вы можете только смоделировать интерфейс объекта, от которого зависит класс, путем внедрения зависимостей на основе сеттера или конструктора. Другим примером может быть попытка смоделировать класс, от которого наследуется тестируемый класс. Вы тоже не можете этого сделать. Возможно, в мире javascript этот тип вещей может быть полезен и с точки зрения некоторых людей желателен, но я думаю, что если вы хотите использовать насмешку, вам нужно придерживаться object
внедрения зависимостей вместо статических use
черт.
Так в чем же альтернатива? Я думаю, что следующий пример будет о том, как использовать, возможно, «традиционные» методы ООП с насмешками, чтобы достичь своей цели - поделиться функциональностью без использования наследования. Этот пример также делает ваши зависимости более явными. И, к сведению, я приветствую вас за то, что вы НЕ используете наследство.
<?php
interface GetterSetter {
public function __call();
}
interface RepositoryProvider {
public function getRepository(string $name);
}
class GetSet implements GetterSetter {
public function __call() {
/* does some magic */
}
}
class DefaultRepository implements RepositoryProvider, GetterSetter {
/**
* @var GetterSetter
*/
private $_getterSetter;
public function __construct(GetterSetter $getterSetter) {
$this->_getterSetter = $getterSetter;
}
public function getRepository(string $name) {
// makes use of the GetSetTrait
$this->__call();
}
public function __call() {
// makes use of the GetSetTrait
$this->_getterSetter->__call();
}
}
class Controller implements RepositoryProvider, GetterSetter {
/**
* @var RepositoryProvider
*/
private $repositoryProvider;
public function __construct() {
$this->repositoryProvider = new DefaultRepository(new GetSet());
}
public function getRepository(string $name) {
return $this->repositoryProvider->getRepository($name);
}
public function __call() {
$this->repositoryProvider->__call();
}
}
В целом я чувствую, что сообщество PHP пошло на дикий левый поворот, пытаясь больше походить на javascript, и я чувствую, что черты могут загнать вас в угол. Это очень хороший пример такого угла. Я бы действительно избегал их, по крайней мере сейчас. В долгосрочной перспективе я считаю, что Generics будет лучшим решением вашей проблемы, связанной с необходимостью генерировать кусок кода GetSet, но в php еще не реализованы генерики :-(.