Я новичок в модульном тестировании и пытаюсь понять, стоит ли мне использовать больше модификатора доступа internal. Я знаю, что если мы воспользуемся internal и установим переменную сборки InternalsVisibleTo, мы сможем тестировать функции, которые не хотим объявлять общедоступными, из проекта тестирования. Это заставляет меня думать, что я должен всегда использовать internal, потому что, по крайней мере, каждый проект (должен?) Иметь свой собственный проект тестирования. Вы можете сказать мне, почему я не должен этого делать? Когда мне следует использовать private?





Вы также можете использовать частные, и вы можете вызывать частные методы с отражением. Если вы используете Visual Studio Team Suite, у него есть хорошая функциональность, которая будет генерировать прокси для вызова ваших частных методов за вас. Вот статья проекта кода, которая демонстрирует, как вы можете самостоятельно выполнить модульное тестирование частных и защищенных методов:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
С точки зрения того, какой модификатор доступа вы должны использовать, мое общее эмпирическое правило - начинать с закрытого и расширять его по мере необходимости. Таким образом вы откроете как можно меньше внутренних деталей вашего класса, что действительно необходимо, и это поможет скрыть детали реализации, как и должно быть.
Если вы хотите протестировать частные методы, взгляните на PrivateObject и PrivateType в пространстве имен Microsoft.VisualStudio.TestTools.UnitTesting. Они предлагают простые в использовании оболочки для необходимого кода отражения.
Документы: PrivateType, PrivateObject
Для VS2017 и 2019 вы можете найти их, загрузив nuget MSTest.TestFramework.
При голосовании против оставьте комментарий. Спасибо.
Глупо голосовать за этот ответ. Это указывает на новое и действительно хорошее решение, о котором ранее не упоминалось.
По-видимому, есть проблема с использованием TestFramework для приложений, ориентированных на .net2.0 или новее: github.com/Microsoft/testfx/issues/366
По умолчанию использовать приватное. Если член не должен отображаться за пределами этого типа, он не должен отображаться за пределами этого типа, даже в рамках того же проекта. Это делает вещи более безопасными и аккуратными - когда вы используете объект, становится яснее, какие методы вы должны использовать.
Сказав это, я считаю разумным иногда делать естественно-частные методы внутренними для тестовых целей. Я предпочитаю это отражению, которое недружелюбно к рефакторингу.
Одна вещь, которую следует учитывать, - это суффикс ForTest:
internal void DoThisForTest(string name)
{
DoThis(name);
}
private void DoThis(string name)
{
// Real implementation
}
Затем, когда вы используете класс в одном проекте, очевидно (сейчас и в будущем), что вам действительно не следует использовать этот метод - он существует только для целей тестирования. Это немного взломано, и я не делаю этого сам, но, по крайней мере, стоит подумать.
Если метод является внутренним, не препятствует ли это его использованию в тестовой сборке?
@ Ральф: не использовать InternalsVisibleTo.
Я иногда использую подход ForTest, но всегда считаю его совершенно уродливым (добавление кода, который не имеет реальной ценности с точки зрения производственной бизнес-логики). Обычно я нахожу, что мне пришлось использовать этот подход, потому что дизайн несколько неудачный (то есть необходимость сбрасывать экземпляры синглтонов между тестами)
Соблазнительно проголосовать против этого - в чем разница между этим взломом и простым превращением класса в внутренний, а не в закрытый? Ну, по крайней мере, с условием компиляции. Тогда это становится действительно беспорядочным.
@CADbloke: Вы имеете в виду сделать метод внутренним, а не приватным? Разница в том, что очевидно, что вы действительно хочу, чтобы быть приватным. Любой код в вашей производственной кодовой базе, который вызывает метод с ForTest, является ошибочным очевидно, тогда как если вы просто сделаете метод внутренним, похоже, что его можно использовать.
Спасибо, я понял, но я был обеспокоен тем, что сам метод теперь доступен в более широком масштабе. Допустим, эта область видимости является «внутренней», поэтому она по-прежнему защищена от любопытных глаз ничего не подозревающей публики, так что никаких отрицательных голосов (мое предположение было немного ошибочным). Я видел ответ в другом месте, в котором рекомендовалось использовать частичные классы, если кто-то привык к этому методу. Еще раз спасибо, что нашли время ответить. :)
@CADbloke: Частичные классы здесь тоже не будут иметь никакого значения. Было бы неплохо иметь атрибут, который инструмент статического анализа мог бы использовать для проверки того, что он не используется из нетестовых сборок.
Единственная причина использовать частичные классы в этом случае - скрыть беспорядок и исключить его из сборки выпуска.
@CADbloke: вы можете исключить отдельные методы в сборке выпуска так же легко в том же файле, что и при использовании частичных классов, IMO. И если вы сделаете это делать, это говорит о том, что вы не запускаете тесты для своей сборки выпуска, что для меня звучит как плохая идея.
Необходимо протестировать внутренние классы и есть атрибут сборки:
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyTests")]
Добавьте это в информационный файл проекта, например Properties\AssemblyInfo.cs.
Мне добавить это в тестовый проект или в тестируемый проект!?!?
Добавьте его в тестируемый проект (например, в Properties \ AssemblyInfo.cs). «MyTests» будет тестовой сборкой.
Это действительно должен быть принятый ответ. Не знаю, как вы, ребята, но когда тесты «слишком далеки» от кода, который они тестируют, я начинаю нервничать. Я полностью за то, чтобы избегать тестирования чего-либо, помеченного как private, но слишком много вещей private вполне могут указывать на класс internal, который изо всех сил пытается извлечь. TDD или без TDD, я предпочитаю иметь больше тестов, которые проверяют много кода, чем иметь несколько тестов, которые проверяют то же количество кода. И отказ от тестирования internal не совсем помогает достичь хорошего соотношения.
Спасибо, Эрик. Искал этот вариант весь день.
Я заключил этот атрибут в оболочку и директиву #if RELEASE, чтобы ничего не было видно в сборке релиза. Просто мысль.
@ s.m. Полностью согласен ... Кроме того, тестировать код пользовательского интерфейса практически невозможно, поэтому я обычно откладываю большую часть своей «пользовательской» логики макета пользовательского интерфейса на методы, которые я могу протестировать без необходимости запуска приложения WPF или WinForms и просмотра результата. (то есть упорядочение элементов, проверка позиционной информации и т. д. и т. д.) Эта логика определенно помечена как внутренняя и абсолютно не могу должна быть протестирована с помощью «общедоступных» средств ... Я терпеть не могу, когда люди говорят о «публичных» 'to' testable ', они нет одни и те же для каждого отдельного случая ... и я сторонник TDD ...
Между @DerickBailey и Дэном Тао идет замечательный обсуждение относительно семантической разницы между внутренний и частный и необходимости тестирования компонентов внутренний. Стоит прочитать.
Обертывание и #if DEBUG, блок #endif включит эту опцию только в отладочных сборках.
Где мне добавить Internal Visible? Основной проект или тестовый проект Класс AssemblyInfo.
Как уже было сказано во втором комментарии: В проекте вы хотите протестировать.
@CADbloke Означает ли это, что вы не запускаете свои модульные тесты для сборки релиза? Мы запускаем модульные тесты как для отладочных, так и для выпускных сборок. Разработчики запускают отладочные модульные тесты локально, сервер сборки запускает их для сборок выпуска. Убедитесь, что мы улавливаем различия между кодом отладки и выпуска, вызванные разными настройками компиляции для обоих (например, оптимизация).
@Marjan Это хороший момент, и да, я изменил способ, которым я это делаю, я тестирую сборки выпуска, а затем комментирую код InternalsVisibleTo для сборки развертывания. Если вы зафиксируете код и пометите его на этом этапе, вы сохраните свою веху для этого развертывания, затем верните закомментированный код и продолжите. Теоретически у этого могут быть побочные эффекты, но это лучше, чем не тестировать код выпуска и код доставки с потенциальной дырой в безопасности.
@CADbloke Классно. Является ли InternalsVisibleTo дырой в безопасности, потому что любая сборка с указанным именем получит доступ к внутренним компонентам? Я полагаю, что .Net по своей сути настолько небезопасен - из-за рефлексии и всех имеющихся (IL) дизассемблеров - что я бы не стал тратить усилия на его удаление для релизной сборки ... Я что-то упустил?
@Marjan Да, если только вы не подписываете сборки строгим именем и не включаете ключ в атрибут InternalsVisibleTo. См. adelkhalil.bloggingabout.net/2007/10/12/…. Думаю, это зависит от вашего уровня паранойи.
Это правильный ответ. Любой ответ, в котором говорится, что модульное тестирование следует проводить только общедоступным методам, упускает суть модульных тестов и дает оправдание. Функциональное тестирование ориентировано на черный ящик. Модульные тесты ориентированы на белый ящик. Им следует тестировать «единицы» функциональности, а не только общедоступные API.
Для .NET Core - добавьте его в любой файл .cs в приложении. Подробности смотрите здесь - stackoverflow.com/a/42235577/968003
Алекс Клаус добавляет это в качестве ответа. Я просто потратил некоторое время на поиски этого. .NET Core достаточно нов, и вопрос требует нового ответа.
Отличное решение для внутренних занятий! Но в VS2017 я все еще получаю сообщение об ошибке, что VS не может создать модульный тест для таких классов. Мне нужно создать его вручную.
С моей точки зрения, это плохой ответ. Внутренние должны оставаться такими, не иметь флага, позволяющего им быть внутренними в зависимости от вызывающего.
Пытаюсь понять, что поставить в параметр InternalsVisibleTo вместо "MyTests". Мой тестовый проект называется api.IntegrationTests. Было бы это [assembly: InternalsVisibleTo("api.IntegrationTests")] ??
@KyleVassella Имя сборки, в которой находятся тесты.
Добавив к ответу Эрика, вы также можете настроить это в файле csproj:
<ItemGroup>
<AssemblyAttribute Include = "System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>MyTests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
Или, если у вас есть один тестовый проект для каждого проекта, который нужно протестировать, вы можете сделать что-то вроде этого в своем файле Directory.Build.props:
<ItemGroup>
<AssemblyAttribute Include = "System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
См .: https://stackoverflow.com/a/49978185/1678053
Пример: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12
Это должен быть главный ответ imo. Все остальные ответы очень устарели, поскольку .net отходит от информации о сборке и перемещает функциональность в определения csproj.
Я использую .NET Core 3.1.101, и дополнения .csproj, которые у меня сработали, были:
<PropertyGroup>
<!-- Explicitly generate Assembly Info -->
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<AssemblyAttribute Include = "System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>MyProject.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
Добавление явной генерации информации о сборке, наконец, заставило меня работать. Спасибо за публикацию!
В .NET Core 2.2 добавьте эту строку в свой Program.cs:
using ...
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyAssembly.Unit.Tests")]
namespace
{
...
Стоит упомянуть - вы часто можете избежать необходимости модульного тестирования ваших внутренних методов, используя
System.Diagnostics.Debug.Assert()внутри самих методов.