Юнит-тесты, internals и Moq

Наверняка многие использовали модификатор 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):

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-объектов.