Замените запечатанный класс

У меня есть класс A, который предоставляет HttpRequestHeaders как свойство. Тестируемый класс — B.

  • B использует A.
  • A также является поддельным классом, который используется только для модульного тестирования.
  • A наследуют интерфейс, который накладывает определение свойства HttpRequestHeaders.

Поэтому мне нужно заменить HttpRequestHeaders, чтобы я мог протестировать B. К сожалению, HttpRequestHeaders является закрытым классом, поэтому его нельзя заменить с помощью NSubstitute:

Не удалось загрузить тип «Castle.Proxies.HttpRequestHeadersProxy_2» из сборки «DynamicProxyGenAssembly2, версия = 0.0.0.0, культура = нейтральная, PublicKeyToken = a621a9e7e5c32e69», поскольку родительский тип запечатан.

Каким было бы общее решение для преодоления этой ситуации?


person mathk    schedule 21.02.2014    source источник
comment
почему бы вам не сломать зависимость между B и A, взаимодействуя с A? Извлеките интерфейс из A и используйте его в B.   -  person Alex Peta    schedule 21.02.2014
comment
@AlexPeta это уже так. Для интерфейса требуется свойство HttpRequestHeaders. Это не меняет проблемы   -  person mathk    schedule 21.02.2014
comment
хорошо, тогда иди на один уровень выше. сделайте свой поддельный HttpRequestHeadersFake : HttpHeaders и используйте его в реализации FakeA   -  person Alex Peta    schedule 21.02.2014
comment
@AlexPeta Почему бы и нет, но HttpRequestHeaders не находится в моей ответственности, это класс System.Net.Http.Headers, поэтому я не очень хочу этого делать. Если бы я мог, я бы не стал отмечать этот класс как запечатанный.   -  person mathk    schedule 21.02.2014
comment
вот исходный код для HttpRequestHeaders, чтобы вам не нужно было писать все целиком: dotnetinside.com/en/framework/Microsoft+ASP.NET/System.Net.Http/. это поможет вам создать поддельный класс. я понимаю о проблеме, что морской свинец. это отстой для модульного тестирования, но вы всегда можете обойти его :)   -  person Alex Peta    schedule 21.02.2014
comment
Да, @AlexPeta, спасибо за ваше решение, я сохраню его, если никто не придумает лучшего. Я действительно не хочу писать интерфейс HttpRequestHeaders   -  person mathk    schedule 21.02.2014
comment
Почему ваш поддельный A не может использовать настоящий объект HttpRequestHeaders — просто создайте объект HttpRequestMessage и получите его свойство Headers?   -  person Damien_The_Unbeliever    schedule 21.02.2014
comment
@Damien_The_Unbeliever, я не могу создать настоящий HttpRequestHeaders, потому что это внутренний $#%@@&5. И захват его с другого объекта может быть решением, но это выглядит немного неуклюжим. Во всяком случае, это кажется более подходящим решением. Спасибо. Пожалуйста, напишите это как правильный ответ, и я приму его.   -  person mathk    schedule 21.02.2014


Ответы (2)


Вместо того, чтобы пытаться найти способ подделать объект sealed, я бы вместо этого посмотрел, смогу ли я получить его реальный экземпляр, предполагая, что у него не слишком много зависимостей.

С другой стороны, HttpRequestHeaders не имеет слишком много зависимостей. С другой стороны, у него есть только конструктор internal. К счастью, HttpRequestMessage может быть свободно создан и предоставляет свойство Headers, которое выполнит требуемое построение за вас.

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

person Damien_The_Unbeliever    schedule 21.02.2014

В зависимости от используемой версии Visual Studio попробуйте подделки (http://msdn.microsoft.com/en-us/library/hh549175.aspx)

Это особенно полезно, если у вас нет контроля над зависимостями.

person iggymoran    schedule 21.02.2014
comment
Вы проверили, что Fake не будет вести себя так же, как NSubstitute? - person mathk; 21.02.2014
comment
У меня нет опыта работы с NSubstitute, но Fakes позволяет подделать любой класс, даже если он запечатан. Он даже не должен быть основан на интерфейсе. Это отлично подходит для введения модульного тестирования в устаревший код, где у вас есть тип ограничений, которые вы описываете. - person iggymoran; 21.02.2014
comment
Может быть решением, но поскольку я использую NSubstitute, я не хочу, чтобы мой проект загружал его еще одной структурой. Спасибо - person mathk; 21.02.2014