Тестирование создания объектов в фабричном методе

В данный момент я пишу тестовый проект и застрял в тестировании следующего поведения.

Я получил интерфейс под названием Menu к которому можно динамически добавлять записи через addEntry-Метод. Есть еще один класс, который содержит Menu объект. Давайте назовем это MenuPresenter, Когда конкретный метод (например, someAction(string title)) называется MenuPresenter следует создать экземпляр Entry объект с полученным названием и добавить его в Menu объект. Создание экземпляра Entry объект происходит в заводском методе внутри MenuPresenter,

введите описание изображения здесь

Проверенное поведение должно быть WhenSomeActionIsCalledShouldAddAnEntryContainingTitleToMenu

Но я не нахожу правильный способ написать тест. Я выяснил две основные возможности его тестирования, но на самом деле они мне не нравятся из-за (позже) упомянутых недостатков.

  1. Реализовать MenuSpy наследование от Menu, который имеет getAddedEntry-Метод. Таким образом, вы можете извлечь добавленное Entry и проверить состояние объекта
    EXPECT_TRUE(entry->getTitle() == title);

Недостатки: Чтобы проверить состояние Entry У меня есть объект, чтобы либо расширить API интерфейса интерфейсными методами, которые используются только для целей тестирования, либо использовать открытые переменные-члены. Это позволяет каждому клиенту получить доступ к внутренней структуре каждой реализации Entry, Таким образом, клиенты связаны с внутренней структурой.

  1. Расширить систему через EntryFactory интерфейс, который имеет
    makeEntry(std::string title)-метод. Как это можно реализовать EntryFactorySpy и можете проверить, если makeEntry-метод вызывается с правильными параметрами (заголовок). В другом тесте мы можем реализовать EntryFactoryStub который возвращает конкретный Entry объект. Тогда мы можем проверить (через MenuSpy) если MenuPresenter добавил полученную запись с завода в меню. Создание экземпляра Entry Затем объект тестируется в модульном тесте на заводе.

Недостатки: Потому что я проверяю вызов фабрики makeEntry— Исправлен алгоритм использования фабрики для создания записей. Испытание тесно связано с внутренней структурой MenuPresenter, Изменение алгоритма (например, использование теперь фабричного метода нарушило бы тест, без ожидаемого поведения приложения.

Для поведения приложения не важно, если MenuPresenter создает Entry само по себе, если он использует EntryFactory, Вот почему я предпочел бы первый путь. Но я не хочу, чтобы клиент Entry быть связанным с внутренней структурой Entry только по причинам тестирования.

Это только пример моей проблемы. На самом деле запись не только создается со строкой. Он получает другие сложные объекты как shared_ptr. Это еще одна причина, почему я не хочу использовать общедоступные методы получения. Таким образом, можно извлечь сложный объект из Entry и измените его (да, я мог бы выдать const shared_ptr, но мне кажется, что это не очень хорошая практика)

Вопрос в том, знает ли кто-нибудь шаблон тестирования или решение, которое решает мою проблему? Значение тестирования, если правильный Entry объект добавлен в Menuобъект, не будучи связанным с жестким алгоритмом или внутренней структурой Entry?

3

Решение

Я Java-разработчик, я никогда не использую TDD в C ++, но, возможно, я могу описать то, что вы хотите протестировать, как вы упоминали выше.

В классическом TDD, MenuPresenter должно быть больше похоже на мини-интеграционный тест, как сказал Мартин Фаулер в Испытание изоляции.

По сути, классические тесты xunit — это не только модульные тесты, но и мини-интеграционные тесты. В результате многим людям нравится тот факт, что клиентские тесты могут отлавливать ошибки, которые, возможно, пропущены основными тестами для объекта, особенно в областях исследования, где взаимодействуют классы.

В Классическом TDD Используйте реальные объекты, если это возможно, и Test Double если неловко использовать реальную вещь при тестировании MenuPresenter, Так что спросите себя, какой ожидаемый эффект происходит, когда Entry добавлено в Menu, тестирование Menu будет добавлять под названием Entryтолько бессмысленно, вы должны проверить, что такое поведение после Entry добавлено Entry добавлено будет отображаться как под названием MenuItemтак ты просто отстаиваешь реальный Menu объект, который отображается MenuItem с ожидаемым названием.

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

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

если вы нашли Menu это неудобно, вы можете использовать MenuSpy вместо этого, а затем написать некоторые IntegrationContractTest в MenuSpy & MenuImpl, как вы можете видеть, что тест с использованием MenuSpy немного выше связь с реализацией, чем реальная Menuпотому что мы ожидаем титулованного Entry был добавлен в MenuSpy не последствия Menu после титулованного Entry добавлено.

пусть MenuSpy чтобы проверить, есть ли титул Entry был добавлен, например:

someAction(title);

menuSpy->hasReceivedATitledEntry(title);

ИЛИ ЖЕ: вы можете проверить добавленный Entry является ли CheckableEntry в getAddedEntryTitle Метод перед возвратом заголовка.

someAction(title);

EXPECT_TRUE(menuSpy->getAddedEntryTitle() == title);
0

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

Других решений пока нет …

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