Считается ли плохой практикой использовать InternalsVisibleTo для кода модульного тестирования?

#c# #.net #unit-testing

#c# #.net #модульное тестирование

Вопрос:

Пример кода в фреймворке AssemblyInfo.cs :

 [assembly: System.Runtime.CompilerServices.InternalsVisibleTo
                          ("Test.Company.Department.Core")]
  

Это плохая практика?

Ответ №1:

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

Комментарии:

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

2. @Rickard: Я категорически не согласен: общедоступный API предназначен для легкого использования другими. Тестирование всей функциональности внутренних типов через общедоступный API, по крайней мере, громоздко. За каждым не таким уж маленьким интерфейсом API может скрываться целый граф объектов со сложными взаимодействиями и ветвями. Тестирование общедоступного API больше похоже на интеграционный тест.

3. итак, вы чувствуете себя нормально, тестируя закрытые элементы и методы?

4. Нет. Разница в размере. Вы не можете сравнить частный метод с внутренним классом или даже с полным графом объектов.

5. Я могу согласиться с этим. Мой разумный аргумент в пользу того, что это нормально, — это простой прагматизм: если вы не можете писать разумные целенаправленные тесты с разумными затратами усилий при простом использовании общедоступного API, используйте InternalsVisibleTo . В противном случае используйте общедоступный API.

Ответ №2:

Лично я думаю, что это нормально. Я никогда не соглашался с догмой «тестировать только общедоступные методы». Я думаю, что хорошо также проводить тестирование в черном ящике, но тестирование в белом ящике может позволить вам протестировать больше сценариев с помощью более простых тестов, особенно если ваш API достаточно «объемный», а общедоступные методы на самом деле выполняют довольно много работы.

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

Ответ №3:

InternalsVisibleTo может быть полезно, если вам нужно протестировать подразделы вашего API, которые вы не хотите раскрывать.

Однако тестирование через общедоступный API предпочтительнее, поскольку это упрощает рефакторинг внутренних API. Используйте InternalsVisibleTo с осторожностью и только при необходимости, например, размер API значителен.

Комментарии:

1. «… это упрощает рефакторинг внутренних API» : конкретный момент для рассмотрения, спасибо.

Ответ №4:

Я думаю, что это вполне разумно.

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

Комментарии:

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

Ответ №5:

Нет, при правильном использовании, потому что в некоторых сценариях это необходимо. Например, у вас есть модульный тест A , который должен протестировать открытый элемент assembly B , который использует некоторый внутренний тип, также определенный в assembly B . Модульный тест нуждается в этом типе, поэтому вы должны использовать InternalsVisibleTo .

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

Ответ №6:

Я бы сказал, что это плохая практика, потому что, если вы использовали принципы SOLID, вам действительно не нужно использовать «InternalsVisibleTo». Однако я знаю, что в «реальном мире» вы получаете всевозможные code…..so прагматичный подход лучше всего подходит.

Также «InternalsVisibleTo» не идеально подходит в сценариях, где вы используете запутывание. Обфускаторы, как правило, запутывают «внутренний» материал. Поэтому любая внешняя dll, которая пытается ссылаться на внутренние компоненты запутанной dll, завершится неудачей. Очевидно, что вы можете настроить обфускатор так, чтобы он игнорировал элементы, на которые ссылаются извне, но это снизило бы эффективность любого обфускатора.

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