C# "внутренний" модификатор доступа при модульном тестировании

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

Стоит упомянуть - вы часто можете избежать необходимости модульного тестирования ваших внутренних методов, используя System.Diagnostics.Debug.Assert() внутри самих методов.

Mike Marynowski 30.03.2017 05:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
519
1
153 081
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Вы также можете использовать частные, и вы можете вызывать частные методы с отражением. Если вы используете 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.

При голосовании против оставьте комментарий. Спасибо.

Brian Rasmussen 30.07.2012 20:44

Глупо голосовать за этот ответ. Это указывает на новое и действительно хорошее решение, о котором ранее не упоминалось.

Ignacio Soler Garcia 06.09.2012 11:43

По-видимому, есть проблема с использованием TestFramework для приложений, ориентированных на .net2.0 или новее: github.com/Microsoft/testfx/issues/366

Johnny Wu 16.07.2019 03:42

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

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

Одна вещь, которую следует учитывать, - это суффикс ForTest:

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Затем, когда вы используете класс в одном проекте, очевидно (сейчас и в будущем), что вам действительно не следует использовать этот метод - он существует только для целей тестирования. Это немного взломано, и я не делаю этого сам, но, по крайней мере, стоит подумать.

Если метод является внутренним, не препятствует ли это его использованию в тестовой сборке?

Ralph Shillington 22.02.2010 18:47

@ Ральф: не использовать InternalsVisibleTo.

Jon Skeet 22.02.2010 19:35

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

ChrisWue 03.04.2012 22:53

Соблазнительно проголосовать против этого - в чем разница между этим взломом и простым превращением класса в внутренний, а не в закрытый? Ну, по крайней мере, с условием компиляции. Тогда это становится действительно беспорядочным.

CAD bloke 06.11.2013 14:18

@CADbloke: Вы имеете в виду сделать метод внутренним, а не приватным? Разница в том, что очевидно, что вы действительно хочу, чтобы быть приватным. Любой код в вашей производственной кодовой базе, который вызывает метод с ForTest, является ошибочным очевидно, тогда как если вы просто сделаете метод внутренним, похоже, что его можно использовать.

Jon Skeet 06.11.2013 14:28

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

CAD bloke 06.11.2013 14:34

@CADbloke: Частичные классы здесь тоже не будут иметь никакого значения. Было бы неплохо иметь атрибут, который инструмент статического анализа мог бы использовать для проверки того, что он не используется из нетестовых сборок.

Jon Skeet 06.11.2013 14:36

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

CAD bloke 06.11.2013 14:42

@CADbloke: вы можете исключить отдельные методы в сборке выпуска так же легко в том же файле, что и при использовании частичных классов, IMO. И если вы сделаете это делать, это говорит о том, что вы не запускаете тесты для своей сборки выпуска, что для меня звучит как плохая идея.

Jon Skeet 06.11.2013 14:54
Ответ принят как подходящий

Необходимо протестировать внутренние классы и есть атрибут сборки:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Добавьте это в информационный файл проекта, например Properties\AssemblyInfo.cs.

Мне добавить это в тестовый проект или в тестируемый проект!?!?

akuhn 09.07.2010 04:39

Добавьте его в тестируемый проект (например, в Properties \ AssemblyInfo.cs). «MyTests» будет тестовой сборкой.

EricSchaefer 09.07.2010 09:32

Это действительно должен быть принятый ответ. Не знаю, как вы, ребята, но когда тесты «слишком далеки» от кода, который они тестируют, я начинаю нервничать. Я полностью за то, чтобы избегать тестирования чего-либо, помеченного как private, но слишком много вещей private вполне могут указывать на класс internal, который изо всех сил пытается извлечь. TDD или без TDD, я предпочитаю иметь больше тестов, которые проверяют много кода, чем иметь несколько тестов, которые проверяют то же количество кода. И отказ от тестирования internal не совсем помогает достичь хорошего соотношения.

s.m. 28.05.2012 11:50

Спасибо, Эрик. Искал этот вариант весь день.

Phil Murray 10.07.2013 19:59

Я заключил этот атрибут в оболочку и директиву #if RELEASE, чтобы ничего не было видно в сборке релиза. Просто мысль.

CAD bloke 07.11.2013 02:27

@ s.m. Полностью согласен ... Кроме того, тестировать код пользовательского интерфейса практически невозможно, поэтому я обычно откладываю большую часть своей «пользовательской» логики макета пользовательского интерфейса на методы, которые я могу протестировать без необходимости запуска приложения WPF или WinForms и просмотра результата. (то есть упорядочение элементов, проверка позиционной информации и т. д. и т. д.) Эта логика определенно помечена как внутренняя и абсолютно не могу должна быть протестирована с помощью «общедоступных» средств ... Я терпеть не могу, когда люди говорят о «публичных» 'to' testable ', они нет одни и те же для каждого отдельного случая ... и я сторонник TDD ...

Robert Petz 04.12.2013 22:02

Между @DerickBailey и Дэном Тао идет замечательный обсуждение относительно семантической разницы между внутренний и частный и необходимости тестирования компонентов внутренний. Стоит прочитать.

Kris McGinnes 21.01.2014 09:22

Обертывание и #if DEBUG, блок #endif включит эту опцию только в отладочных сборках.

The Real Edward Cullen 04.02.2014 20:33

Где мне добавить Internal Visible? Основной проект или тестовый проект Класс AssemblyInfo.

Neeraj Dubey 12.03.2014 16:25

Как уже было сказано во втором комментарии: В проекте вы хотите протестировать.

EricSchaefer 13.03.2014 11:18

@CADbloke Означает ли это, что вы не запускаете свои модульные тесты для сборки релиза? Мы запускаем модульные тесты как для отладочных, так и для выпускных сборок. Разработчики запускают отладочные модульные тесты локально, сервер сборки запускает их для сборок выпуска. Убедитесь, что мы улавливаем различия между кодом отладки и выпуска, вызванные разными настройками компиляции для обоих (например, оптимизация).

Marjan Venema 08.03.2015 13:17

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

CAD bloke 08.03.2015 13:37

@CADbloke Классно. Является ли InternalsVisibleTo дырой в безопасности, потому что любая сборка с указанным именем получит доступ к внутренним компонентам? Я полагаю, что .Net по своей сути настолько небезопасен - из-за рефлексии и всех имеющихся (IL) дизассемблеров - что я бы не стал тратить усилия на его удаление для релизной сборки ... Я что-то упустил?

Marjan Venema 08.03.2015 13:41

@Marjan Да, если только вы не подписываете сборки строгим именем и не включаете ключ в атрибут InternalsVisibleTo. См. adelkhalil.bloggingabout.net/2007/10/12/…. Думаю, это зависит от вашего уровня паранойи.

CAD bloke 08.03.2015 13:53

Это правильный ответ. Любой ответ, в котором говорится, что модульное тестирование следует проводить только общедоступным методам, упускает суть модульных тестов и дает оправдание. Функциональное тестирование ориентировано на черный ящик. Модульные тесты ориентированы на белый ящик. Им следует тестировать «единицы» функциональности, а не только общедоступные API.

Gunnar 19.08.2015 02:00

Для .NET Core - добавьте его в любой файл .cs в приложении. Подробности смотрите здесь - stackoverflow.com/a/42235577/968003

Alex Klaus 02.11.2017 04:13

Алекс Клаус добавляет это в качестве ответа. Я просто потратил некоторое время на поиски этого. .NET Core достаточно нов, и вопрос требует нового ответа.

Sentinel 16.01.2018 02:11

Отличное решение для внутренних занятий! Но в VS2017 я все еще получаю сообщение об ошибке, что VS не может создать модульный тест для таких классов. Мне нужно создать его вручную.

Markus L 09.02.2018 12:42

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

motoDrizzt 24.05.2018 12:11

Пытаюсь понять, что поставить в параметр InternalsVisibleTo вместо "MyTests". Мой тестовый проект называется api.IntegrationTests. Было бы это [assembly: InternalsVisibleTo("api.IntegrationTests")] ??

Kyle Vassella 26.10.2018 19:31

@KyleVassella Имя сборки, в которой находятся тесты.

EricSchaefer 31.10.2018 10:48

Добавив к ответу Эрика, вы также можете настроить это в файле 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.

mBrice1024 17.05.2020 18:54

Я использую .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>

Добавление явной генерации информации о сборке, наконец, заставило меня работать. Спасибо за публикацию!

thevioletsaber 11.03.2020 19:16

В .NET Core 2.2 добавьте эту строку в свой Program.cs:

using ...
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("MyAssembly.Unit.Tests")]

namespace
{
...

Другие вопросы по теме