2011-12-17 6 views
7

Я получаю доступ к PHPUnit и до сих пор нашел его довольно простым в использовании, но я столкнулся с тестовым примером, который вызывает у меня трудности.PHPUnit: Stubbing multiple interfaces

Я пишу код против набора интерфейсов, которые, как предполагается, будут реализовывать объекты (некоторые из них, некоторые самодельные), а для SUT требуется входной объект для реализации нескольких интерфейсов. Например:

class MyClass implements ArrayAccess, MyInterface 
{ 
    // ... 
} 

тестируемые делают вещи, такие как это:

class ClassToBeTested 
{ 
    protected $obj = NULL; 

    public function __construct ($obj) 
    { 
     $this -> obj = $obj; 
    } 

    public function methodToBeTested() 
    { 
     if ($this -> obj instanceof ArrayAccess) 
     && ($this -> obj instanceof MyInterface) 
     { 
      // ... 
     } 
    } 

    public function otherMethodUnderTest() 
    { 
     if ($this -> obj instanceof ArrayAccess) 
     { 
      // ... 
     } 
     else 
     if ($this -> obj instanceof MyInterface) 
     { 
      // ... 
     } 
    } 
} 

я могу создать заглушку из одного интерфейса или других, но я не знаю, если вы можете создать огрызок, который реализует их оба.

protected function setUp() 
{ 
    $stubField = $this -> getMockBuilder ('ArrayAccess') 
      -> getMock(); 
    $this -> object = new ClassToBeTested ($stubField); 
} 

или

protected function setUp() 
{ 
    $stubField = $this -> getMockBuilder ('MyInterface') 
      -> getMock(); 
    $this -> object = new ClassToBeTested ($stubField); 
} 

Можно ли генерировать окурки из списка интерфейсов, или у меня есть незавершенная конкретный класс, который реализует ожидаемые интерфейсы? Это само по себе вызывает трудности, потому что классу, который нужно окутать, нужен другой объект, который должен быть передан его конструктору, и я не могу заставить disableOriginalConstructor() или setConstructorArgs() работать, я думаю, это потому, что конкретные классы, о которых идет речь, не реализуют сам конструктор, а наследуют его от суперкласса. Я пропустил что-то очевидное здесь?

ответ

6

У вас есть доступ к редактированию исходного кода? Если это так, я бы создал новый интерфейс, который расширяет как ArrayAccess, так и MyInterface. Таким образом, вы должны иметь возможность заглушить/издеваться над объектом, чтобы протестировать тестируемый метод.

+0

+1 Потому что это такая кровавая очевидная идея, о которой я должен был подумать. Тем не менее я собираюсь оставить его открытым немного дольше, прежде чем принимать, чтобы посмотреть, что другие предлагают. – GordonM

+0

Персоналии предпочитают @Maksim Kotlyar ответить как введение интерфейса в вашу прикладную кодовую базу в качестве заглушки для вашего теста, будет бесполезно менять ваш производственный код. –

+0

@YohanG. В его нынешнем виде, возможно, да. Глядя на это снова (спустя более 4,5 лет!), Я думаю, что проблема, которую имеет ОП, на самом деле является симптомом более глубокой проблемы. $ Obj, который он передаёт CUT, может либо реализовать ArrayAccess, либо MyInterface. Мне кажется, что есть другая концепция, пытающаяся выйти. То, что он должен делать, - это захват одного интерфейса/класса в его CUT и позволяющий абстрагировать разницу между ними. Таким образом, избавиться от этих ужасных, если ... instanceof блоков и быть более полиморфными. – liquorvicar

2

Не рекомендуется создавать интерфейс в прикладной кодовой базе до , чтобы сделать тесты счастливыми. Я имею в виду, что вы можете создать этот интерфейс, но было бы лучше, если бы вы поместили его где-нибудь в базу тестового кода. Например, вы можете поместить интерфейс после класса тестового случая в файле непосредственно

Для проверки двух интерфейсов одновременно я создал интерфейс в тест дела (это может быть любое другое место)

interface ApiAwareAction implements ActionInterface, ApiAwareInterface 
{ 
} 

И после того, как я сделал макет этого класса:

$this->getMock('Payum\Tests\ApiAwareAction'); 
+2

это то же самое, что и создание другого интерфейса, расширяющего оба интерфейса. Но противнее. – Talus

+0

Моим главным моментом было поместить его в тестовое пространство имен, тот же файл, что и тестовый пример, в котором вы его будете использовать. Вы не должны указывать в код приложения интерфейс, который вам нужен только для тестов, которые указывают. С точки зрения реализации он может быть интерфейсом –

+0

Еще раз, использование нового интерфейса, который расширяет оба этих интерфейса, по-прежнему остается. Ваш реферат не является ни реализацией (даже для тестов), ни интерфейсом. Когда вы тестируете метод в зависимости от интерфейса, у вас нет единой заботы в мире о том, как это реализовано. Следовательно, создание интерфейса, расширяющего оба интерфейса, означает «что-то [здесь, mock] реализует этот интерфейс, который также реализует оба моих необходимых интерфейса». Сообщение более четкое, чем «Что-то расширяет абстрактный текст, который просит реализовать оба интерфейса» – Talus