Подтвердить что ты не робот

Рекомендуется ли издеваться над конкретным классом?

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

Но на самом деле я видел вместо этого определенный класс разработчиков. Рекомендуется ли издеваться над конкретным классом?

4b9b3361

Ответ 1

В теории абсолютно нет проблем, издевающихся над конкретным классом; мы тестируем логический интерфейс (а не ключевое слово interface), и неважно, обеспечивается ли этот логический интерфейс с помощью class или interface.

На практике .NET/С# делает это немного проблематичным. Как вы уже сказали,.NET mocking framework я собираюсь предположить, что вы ограничены этим.

В .NET/С# члены по умолчанию не виртуальны, поэтому любые прокси-методы издевательского поведения (т.е. вывод из класса и переопределение всех членов, чтобы делать тестовые данные) не будут работать, если вы явно не используете отметьте элементы как virtual. Это приводит к проблеме: вы используете экземпляр издевающегося класса, который должен быть полностью безопасным в вашем unit test (т.е. Не будет запускать какой-либо реальный код), но если вы не убедитесь, что все есть virtual, вы может закончиться сочетанием реального и запутанного кода (это может быть особенно проблематичным, если есть логика конструктора, которая всегда выполняется, и усугубляется, если есть другие конкретные зависимости, которые должны быть новыми).

Есть несколько способов обойти это.

  • Используйте interfaces. Это работает, и это то, что мы советуем в документации NSubstitute, но имеет недостаток потенциально раздувания вашей базы кода с интерфейсами, которые на самом деле не нужны. Возможно, если мы найдем хорошие абстракции в нашем коде, мы, естественно, получим аккуратные интерфейсы, которые можно использовать повторно, с которыми мы можем протестировать. Я не совсем видел, как это происходит, но YMMV.:)
  • Успокойтесь, чтобы все было виртуально. Спорным недостатком этого является то, что мы предлагаем, чтобы все эти члены были точками расширения в нашем дизайне, когда мы действительно хотим изменить поведение всего класса для тестирования. Он также не останавливает выполнение логики конструктора и не помогает, если конкретный класс требует других зависимостей.
  • Используйте перезапись сборки через нечто вроде надстройки Virtuosity для Fody, которая вы можете использовать для изменения всех членов класса в своей сборке, чтобы быть виртуальными.
  • Использовать смехотворную библиотеку, отличную от прокси-сервера, например TypeMock (оплачивается), JustMock (оплачивается), Microsoft Fakes (требуется VS Ultimate/Enterprise, хотя его предшественник, Microsoft Moles, является бесплатным) или Prig (бесплатный + открытый исходный код). Я считаю, что они способны издеваться над всеми аспектами классов, а также с статическими членами.

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

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

Надеюсь, что это поможет.

Ответ 2

Обновление:

Спустя 3 года я хочу признать, что передумал.

Теоретически я до сих пор не люблю создавать интерфейсы, чтобы облегчить создание фиктивных объектов. На практике (я использую NSubstitute) гораздо проще использовать Substitute.For<MyInterface>(), чем имитировать реальный класс с несколькими параметрами, например, Substitute.For<MyCLass>(mockedParam1, mockedParam2, mockedParam3), где каждый параметр должен быть смоделирован отдельно. Другие потенциальные проблемы описаны в документации Замены

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

Оригинальный ответ:

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

Отhttp://blog.ploeh.dk/2010/12/02/InterfacesAreNotAbstractions.aspx

Вы извлекаете интерфейсы из ваших классов, чтобы включить свободный связь? Если это так, у вас, вероятно, есть отношения1:1 между интерфейсы и конкретные классы, которые их реализуют. Это, вероятно, не очень хороший знак и нарушаетповторно используемые абстракции Принцип (RAP).

Наличие только одной реализации данного интерфейса - это запах кода.

Если ваша цель - тестируемость, я предпочитаю второй вариант из ответа Дэвида Чепака выше.

Однако я не уверен, что вы должны сделать все виртуальным. Достаточно сделать виртуальными только те методы, которые вы собираетесь заменить. Я также добавлю комментарий рядом с объявлением метода о том, что метод является виртуальным, чтобы сделать его заменяющим для насмешливого тестирования модуля.

Однако обратите внимание, что замена конкретных классов вместо интерфейсов имеет некоторые ограничения. Например. для NSubstitute

Примечание: рекурсивные заменители не будут созданы для классов, так как создание и использование классов может иметь потенциально нежелательные побочные эффекты

,

Ответ 3

Вопрос скорее: почему бы и нет?

Я могу подумать о нескольких сценариях, где это полезно, например:

Реализация конкретного класса еще не завершена, или парень, который сделал это, ненадежен. Поэтому я издеваюсь над классом, поскольку он указан и проверяет мой код на него.

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

Ответ 4

Не то, чтобы это было рекомендовано, так что вы можете сделать это, если у вас нет другого выбора.

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