Сделать код внутренним, но доступным для модульного тестирования из других проектов

129

Мы помещаем все наши модульные тесты в свои собственные проекты. Мы обнаруживаем, что нам нужно сделать определенные классы общедоступными, а не внутренними только для модульных тестов. Есть ли способ избежать этого? Каковы последствия для памяти, если сделать классы общедоступными, а не закрытыми?

Лиора
источник

Ответы:

205

Если вы используете .NET, атрибут сборки InternalsVisibleTo позволяет создавать «дружественные» сборки. Это конкретные сборки со строгими именами, которым разрешен доступ к внутренним классам и членам другой сборки.

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

Пример:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
ясень
источник
23
Я бы предложил поставить #if DEBUG вокруг атрибута, а затем выполнить модульное тестирование при отладке. Таким образом вы будете уверены, что атрибут не установлен в коде выпуска.
Стив Кук
Это просто идея, я не знаю .... Как насчет: #if DEBUG открытый класс IniReader # другой внутренний класс IniReader #endif Наверное, не рекомендуется? Зачем?
jmelhus
4
Зачем ограничивать тесты отладочными сборками?
Marco Mp
2
Кроме того, просто придирка, нет необходимости в строгих именах "дружественных" сборок (может быть хорошей практикой - даже если мой личный вкус говорит об обратном, но не обязательно).
Marco Mp
9
Не согласен. Если я создаю сложный компонент с очень тонким общедоступным API, непрактично и нереально тестировать только через общедоступный API. В итоге вы получите непослушный комок грязи. Вместо этого я бы тщательно определил внутренние блоки и тестировал их отдельно. Как часто говорил Джереми Д. Миллер: «Тестируйте маленькое, прежде чем проверять большое».
Деннис Думен,
6

Если это внутренний класс, его нельзя использовать изолированно. Поэтому вам не следует тестировать его, кроме тестирования какого-либо другого класса, который использует этот объект внутри.

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

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

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

мистифицировать
источник
2
Детали реализации должны быть проверены как часть всеобъемлющего теста. Не заглядывайте в частные переменные ... проверьте ожидаемое поведение. Если проверка верна ... вся внутренняя сантехника и проводка должны быть проверены как часть этого. Проголосовал.
Gishu
69
Я не обязательно согласен с этим, поскольку эти классы являются «общедоступными» для других классов внутри DLL, и функциональность класса должна быть протестирована независимо
Леора
27
Я тоже не согласен. Единицы есть единицы, и они должны тестироваться изолированно.
Sentinel
5
Отличное общее руководство, но давайте не будем догадываться. Тесты служат двум основным целям: 1) Регрессия - убедиться, что вы что-то не сломали, 2) Увеличение скорости разработки. Если мне придется поддерживать огромную службу каждый раз, когда я хочу написать строчку кода, я буду тормозить разработку. Если у меня сложная внутренняя часть, я хочу иметь возможность разрабатывать и тестировать изолированно. И наоборот, я не хочу, чтобы все тестировалось изолированно (тем самым уменьшая значение регрессионного тестирования или дублируя тестовый код), но некоторый код оправдывает изоляцию. Возможно, ключ в том, чтобы сделать его отдельной сборкой.
Ли Дженсен
2
ОП здесь правильный. Вы связываете свои тесты с внутренней реализацией. Следовательно, ваша команда навсегда останется рабом исправления тестов и ужасного издевательского кода. Тестируйте только общедоступные API, будь то API из упакованных библиотек или API, доступные в сети. Все коды должны быть доступны через входную дверь. Реализация тестирования - вот почему TDD в значительной степени умер, это просто огромная PITA для тестирования буквально каждого класса всего приложения, вместо того, чтобы сосредоточиться на обеспечении поведения системы. Я думаю, что почти все, включая «авторитетные» книги, ошибались или не разъясняли этого.
Люк Пуплетт
4

для документации

в качестве альтернативы вы можете создать экземпляр внутреннего класса с помощью Type.GetTypeметода

пример

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

для универсального типа есть другой процесс, как показано ниже:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});
ktutnik
источник
-5

Классы могут быть как общедоступными, так и запечатанными.

Но не делай этого.

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

Изменить: я имею в виду, если вы не хотите включать какие-либо тестовые материалы в исходную сборку; это также работает, если участники являются частными.

TraumaPony
источник
1
Чего ждать? Вы говорите, не делайте public sealed class? Что вы думаете об этом драгоценном камне?
Crush
@crush traumapony не входил в систему с 2009 года.
Проф. Фалькен,