2011-12-23 1 views
12

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

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

Таким образом, если бы я хотел проверить ClassA, я мог бы передать объект ClassAFactory конструктору тестового класса, и если бы я захотел проверить ClassB, я бы передал объект ClassBFactory. Оба класса предназначены для взаимозаменяемости, и поскольку предполагается, что тестируются только общедоступные методы, это кажется идеальным.

Но как насчет тестирования конструктора? Должен ли я лучше писать абстрактный тестовый класс и внедрять тесты конструктора в классы, наследующие абстрактный тестовый класс (разные классы могут создаваться по-разному)?

Если я использовал первую идею, я думаю, я бы тестовый класс для каждого класса испытывается, как:

class ClassATest extends [PHPUnit test case] 
{ 
    $myFactory = new ClassAFactory(); 
    $myTest = new ClassTest($myFactory); 

    $myTest->test1(); 
    $myTest->test2(); 
    //etc. 
} 

Что лучший способ пойти по этому поводу? Я хочу иметь общий тест, так что, когда я пишу новые классы для реализации общего интерфейса, я могу просто поставить объект тех же тестов, что и для других. Но, видя, что разные классы будут иметь разные конструкторы, возможно, лучше написать абстрактный класс тестов и расширить его для каждого нового объекта? Как вы думаете?

ответ

6

Я думаю, вам нужно пересмотреть свой план. Вы не можете протестировать интерфейс, и есть веская причина - интерфейсы просто определяют API, а не функциональность, тестируют функциональность тестирования. Позвольте мне привести вам пример, который может помочь. Скажем, у вас есть интерфейс обмена сообщениями. Таким образом, вы реализуете EmailMessager и SMSMessager. Теперь вам нужно протестировать их отдельно, как с EmailMessager, вам нужно убедиться, что он делает все возможное, может быть, проверка адресата (адрес электронной почты) и, возможно, делегирование отправки в класс электронной почты и т. Д. Очевидно, что SMS-сообщение будет отличаться.

+1

В этом конкретном примере классы будут представлять получателей для списка рассылки. Хотя у них будут разные способы выполнения своей работы, они будут брать те же данные от клиента и возвращать те же результаты - полностью взаимозаменяемые. Мне кажется, что если объекты из разных классов, которые используют этот интерфейс, взаимозаменяемы внутри клиентского кода, они также должны быть взаимозаменяемы в тестовом коде. Поэтому, когда я говорю, что хочу протестировать интерфейс, я имею в виду, что я хочу написать тест для этого интерфейса, чтобы протестировать класс, который использует этот интерфейс. Это имеет смысл для вас? –

+1

Нет, это все еще не имеет смысла. Я не понимаю, как работает интерфейс. Возможно, вам нужен абстрактный базовый класс, а не интерфейс для реализации общего кода. Я не уверен, что отличается от ваших классов, кроме данных. – liquorvicar

+0

Предположим, что в классе MailChimpRecipient использует интерфейс MailingListRecipient: клиентский код добавляет кого-то в список, задав свойства адреса электронной почты и имя объекта-получателя, а затем вызывая метод их добавления. Теперь этот метод для класса MailChimp будет использовать API MailChimp для их добавления. Скажите в будущем, я хочу разместить собственный список рассылки или использовать другого поставщика, я мог бы переключить класс на другой класс, который использует интерфейс MailingListRecipient. Этот класс может сделать что-то совершенно другое, чтобы добавить получателя, но он все равно берет те же данные. –

6

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

abstract class IAdderTestCase extends PFTC 
{ 
    function testAdd() { 
     self::assertEquals(5, $this->fixture->add(2, 3)); 
    } 
    ... 
} 

class BasicAdderTest extends IAdderTestCase 
{ 
    function setUp() { 
     $this->fixture = new BasicAdder(); 
    } 
} 

PHPUnit будет вызывать setUp() перед каждым методом тестирования. Он должен вызывать все унаследованные методы тестирования для каждого конкретного подкласса плюс любые дополнительные, например. для проверки конструкторов.

+0

Спасибо за это! Это кажется лучшим способом, так как я могу изменить метод setUp для каждого другого подкласса. –

Смежные вопросы