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

114

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

возможный дубликатC# "internal" access modifier when doing unit testing Dour High Arch

Ваш Ответ

4   ответа
-4

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

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

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

Чего ждать? Вы говорите, что не делаетеpublic sealed class? Что вы думаете об этом драгоценном камне?
6

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

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

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

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

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

в качестве альтернативы вы можете создать экземпляр внутреннего класса с помощью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*/});
181

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

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

Example:

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

Похожие вопросы