Как я могу помешать моим модульным тестам требовать знаний о внутренностях реализации при использовании фиктивных объектов? - PullRequest
8 голосов
/ 10 августа 2010

Я все еще нахожусь в стадии обучения в отношении юнит-тестирования и, в частности, в отношении насмешек (я использую платформы PascalMock и DUnit ).Одна вещь, на которую я сейчас наткнулся, заключалась в том, что я не смог найти способ жестко запрограммировать детали реализации тестируемого класса / интерфейса в моем модульном тесте, и это просто неправильно ...

Например: я хочупротестировать класс, который реализует очень простой интерфейс для чтения и записи настроек приложения (в основном, пары имя / значение).Интерфейс, представляемый потребителю, совершенно не зависит от того, где и как на самом деле хранятся значения (например, реестр, INI-файл, XML, база данных и т. Д.).Естественно, уровень доступа реализован еще одним классом, который внедряется в тестируемый класс при создании.Я создал фиктивный объект для этого уровня доступа, и теперь я могу полностью протестировать класс, реализующий интерфейс, фактически не читая и не записывая что-либо в любой реестр / INI-файл / что угодно.

Однако, чтобы гарантироватьmock ведет себя точно так же, как реальная вещь, когда к нему обращается тестируемый класс, мои модульные тесты должны устанавливать фиктивный объект, очень явно определяя ожидаемые вызовы методов и возвращаемые значения, ожидаемые тестируемым классом.Это означает, что если мне когда-либо придется вносить изменения в интерфейс уровня доступа или в способ, которым тестируемый класс использует этот уровень, мне также придется изменить модульные тесты для класса, который внутренне использует этот интерфейс, даже если интерфейс класса, который я тестирую, практически не изменился.Это то, с чем мне придется смириться при использовании mocks, или есть лучший способ создать зависимости класса, чтобы этого избежать?

Ответы [ 3 ]

7 голосов
/ 10 августа 2010

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

Правильно.

Изменения в интерфейсе уровня доступа или в способе, которым тестируемый класс использует этот уровень. Мне также придется изменить модульные тесты

Правильно.

, хотя интерфейс класса, который я тестирую, практически не изменился.

"Фактическое тестирование"?Вы имеете в виду выставленный интерфейсный класс?Все в порядке.

То, как «протестированный» (интерфейсный) класс использует уровень доступа, означает, что вы изменили внутренний интерфейс на уровень доступа.Изменения интерфейса (даже внутренние) требуют изменения теста и могут привести к поломке, если вы сделали что-то не так.

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

Тестирование не должно быть "надежным".Это должно быть хрупким.Если вы сделаете изменение, которое изменит внутреннее поведение, то вещи могут сломаться.Если бы ваши тесты были слишком надежными, они бы ничего не проверяли - они бы просто работали.И это неправильно.

Тесты должны работать только по точной правильной причине.

6 голосов
/ 10 августа 2010

Это то, что мне просто нужно жить с использованием насмешек или там лучший способ разработать классовые зависимости, которых можно избежать это? * * 1002

Часто макеты (особенно чувствительные фреймворки, такие как JMock) вынуждают вас учитывать детали, которые не имеют прямого отношения к поведению, которое вы пытаетесь протестировать, и иногда это может быть даже полезно, предоставляя подозрительный код делать слишком много и имеет слишком много вызовов / зависимостей.

Однако в вашем случае, если я правильно прочитал ваше описание, похоже, у вас действительно нет проблем. Если вы спроектировали слой чтения / записи правильно и с соответствующим уровнем абстракции, вам не нужно его менять.

Это означает, что если бы я когда-либо имел внести изменения в интерфейс уровень доступа или к тому, что протестированный класс использует этот слой I также придется поменять блок тесты для класса, который внутренне использует этот интерфейс, хотя интерфейс класса я на самом деле тестирование вообще не изменилось.

Не стоит ли писать абстрагированный уровень доступа, чтобы избежать этого? В целом, следуя принципу Open / Closed , интерфейс такого рода не должен меняться и не должен нарушать контракт с классом, который его использует, и, соответственно, выигрывает Не нарушайте свои юнит-тесты. Теперь, если вы измените порядок вызовов методов или будете вынуждены делать новые вызовы для абстрагированного слоя, тогда, да, особенно в некоторых средах, ваши ложные ожидания сломаются. Это только часть стоимости использования макетов, и это вполне приемлемо. Но сам интерфейс в целом должен оставаться стабильным.

1 голос
/ 11 августа 2010

Просто добавьте несколько имен в ваш пример,

  • RegistryBasedDictionary реализует словарь ролей (интерфейс).
  • RegistryBasedDictionary зависит от роли RegistryAccessor, реализованного RegistryWinAPIWrapper.

В данный момент вы заинтересованы в тестировании RegistryBasedDictionary.Модульные тесты внедрили бы ложную зависимость для роли RegistryAccessor и протестировали ожидаемое взаимодействие с зависимостями.

  • Уловка, позволяющая избежать ненужного сопровождения тестов, заключается в том, чтобы " точно указать, что должно произойти ... и не более. " (из книга ГСНО (must-read для фиктивного TDD), поэтому, если порядок вызовов методов зависимости не имеет значения, не указывайте его в тесте. Это дает вам свободу изменять порядок вызовов в реализации.)
  • Разработайте роли так, чтобы они не содержали утечек из фактических реализаций - сохраняйте независимость от реализации ролей .

Единственной причиной для изменения тестов RegistryBasedDictionary является изменение поведения RegistryBasedDictionary, а не каких-либо его зависимостей.Поэтому, если его взаимодействие с зависимостями или роли / контракты изменятся, тесты необходимо будет обновить .Это цена тестов, основанных на взаимодействии, которые вы должны заплатить за гибкость тестирования в изоляции.Однако на практике это случается не так часто.

...