Наверняка многие использовали модификатор internal, чтобы скрыть внутренние объекты от других сборок. Рассмотрим одну интересую особенность использования таких классов в проектах с юнит-тестами.
Чтобы сделать internal классы доступными в проекте тестирования используется простое указание InternalsVisibleTo в AssemblyInfo.cs проекта:
[assembly: InternalsVisibleTo("MyProject.UnitTests")]
Это позволит разработчику писать код тестов с их использованием. Вроде бы все хорошо.
Проблема
Однако, это не все что необходимо, если используется библиотека Moq. Часть запускаемых тестов будет падать c исключением TypeLoadException. Если внимательно посмотреть текст сообщения, то можно увидеть что TypeBuilder не может получить доступ к internal классам.
System.TypeLoadException: Access is denied: 'MyProject.DataAccess.Dto.UserDto'.
Это может произойти например вот в таком тестовом коде (здесь repository это Mock<T>):
repository
.Setup(r => r.Add(It.IsAny<UserDto>()))
.Callback<UserDto>(dataStore.Add);
Попытка включить Moq в список InternalsVisibleTo ни к чему не приведет. Заменить internal классы на public поможет решить проблему. Но согласитесь, это не лучший выход с точки зрения построения архитектуры приложения.
Решение
На самом деле нужно указать:
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
Теперь можно запускать тесты.
Немного подробностей
А что же такое DynamicProxyGenAssembly2? Это динамически создаваемая сборка, которая порождается библиотекой CastleProxy и предназначена для генерации прокси-объектов. Как можно легко догадаться, Moq (и, кстати, NSubsitute) используют CastleProxy для порождения mock-объектов.