Как высмеивать запечатанный класс?
Издевательство над закрытыми классами может быть довольно сложной задачей. В настоящее время я предпочитаю шаблон адаптера, чтобы справиться с этим, но что-то в этом остается странным.
Итак, как лучше всего высмеивать закрытые классы?
Ответы на Java более чем приветствуются . Фактически, я ожидал, что сообщество Java занимается этим дольше и может многое предложить.
Но вот некоторые из мнений .NET:
Ответов (11)11
Мое общее эмпирическое правило состоит в том, что объекты, которые мне нужно имитировать, также должны иметь общий интерфейс. Я думаю, что это правильно с точки зрения дизайна и значительно упрощает тестирование (и обычно это то, что вы получаете, если выполняете TDD). Подробнее об этом можно прочитать в последней публикации Google Testing Blog (см. Пункт 9).
Кроме того, последние 4 года я работал в основном на Java, и могу сказать, что могу по одной руке посчитать, сколько раз я создавал финальный (запечатанный) класс. Еще одно правило здесь: у меня всегда должна быть веская причина для запечатывания класса, а не для запечатывания его по умолчанию.
Для .NET вы можете использовать что-то вроде TypeMock , который использует API профилирования и позволяет подключаться к вызовам практически ко всему.
Хотя в настоящее время он доступен только в бета-версии, я думаю, что стоит помнить о функции прокладки новой платформы Fakes (часть бета-версии Visual Studio 11 ).
Типы прокладок обеспечивают механизм обхода любого метода .NET для определенного пользователем делегата. Типы прокладок генерируются кодом генератора Fakes, и они используют делегаты, которые мы называем типами оболочки, для указания новых реализаций методов. Под капотом типы прокладок используют обратные вызовы, которые были введены во время выполнения в тела MSIL методов.
Лично я пытался использовать это для имитации методов в закрытых классах фреймворка, таких как DrawingContext.
Я недавно столкнулся с этой проблемой, и после чтения / поиска в Интернете кажется, что нет простого способа, кроме использования другого инструмента, как упоминалось выше. Или грубо поступать так, как я:
- Создать экземпляр запечатанного класса без вызова конструктора.
System.Runtime.Serialization.FormatterServices.GetUninitializedObject (instanceType);
Присвойте значения вашим свойствам / полям через отражение
- YourObject.GetType (). GetProperty ("Имя свойства"). SetValue (dto, newValue, null);
- YourObject.GetType (). GetField ("Имя поля"). SetValue (dto, newValue);
Вы можете использовать Mocksanity, который является бесплатным и имеет лицензию MIT.
Проблема с TypeMock в том, что он оправдывает плохой дизайн. Теперь я знаю, что часто скрывается чужой плохой дизайн, но включение этого в процесс разработки может очень легко привести к разрешению ваших собственных плохих проектов.
Я думаю, что если вы собираетесь использовать фреймворк для фиксации, вам следует использовать традиционный (например, Moq) и создать слой изоляции вокруг немоделируемого объекта, а вместо этого имитировать слой изоляции.
Обычно я выбираю путь создания интерфейса и класса адаптера / прокси, чтобы облегчить имитацию запечатанного типа. Тем не менее, я также экспериментировал с пропуском создания интерфейса и созданием незапечатанного типа прокси с помощью виртуальных методов. Это хорошо сработало, когда прокси действительно является естественным базовым классом, который инкапсулирует и использует часть запечатанного класса.
Имея дело с кодом, который требовал такой адаптации, я устал выполнять одни и те же действия для создания интерфейса и типа прокси, поэтому я реализовал библиотеку для автоматизации задачи.
Код несколько сложнее, чем пример, приведенный в статье, на которую вы ссылаетесь, поскольку он создает сборку (вместо исходного кода), позволяет выполнять генерацию кода для любого типа и не требует такой большой настройки.
Для получения дополнительной информации перейдите на эту страницу .
Я почти всегда избегаю зависимости от внешних классов глубоко в моем коде. Вместо этого я бы предпочел использовать адаптер / мост, чтобы поговорить с ними. Таким образом, я имею дело со своей семантикой, и боль перевода изолирована в одном классе.
Это также упрощает переключение моих зависимостей в долгосрочной перспективе.
Я считаю, что Moles из Microsoft Research позволяет вам это делать. Со страницы Родинок:
Кроты могут использоваться для обхода любого метода .NET, включая невиртуальные / статические методы в запечатанных типах.
ОБНОВЛЕНИЕ: в предстоящем выпуске VS 11 есть новый фреймворк под названием «Fakes», предназначенный для замены Moles:
Подделки Framework в Visual Studio 11 является следующим поколением родинок и окурки, и в конечном итоге заменить его. Однако подделки отличаются от кротов, поэтому переход от кротов к подделкам потребует некоторых изменений в вашем коде. Руководство по этой миграции будет доступно позже.
Требования : Visual Studio 11 Ultimate, .NET 4.5
Вполне разумно издеваться над запечатанным классом, потому что многие классы фреймворка запечатаны.
В моем случае я пытаюсь издеваться над классом MessageQueue .Net, чтобы я мог TDD свою изящную логику обработки исключений.
Если у кого-то есть идеи о том, как преодолеть ошибку Moq, касающуюся «Недопустимой настройки на непереопределяемом члене», пожалуйста, дайте мне знать.
код:
[TestMethod]
public void Test()
{
Queue<Message> messages = new Queue<Message>();
Action<Message> sendDelegate = msg => messages.Enqueue(msg);
Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate =
(v1, v2) =>
{
throw new Exception("Test Exception to simulate a failed queue read.");
};
MessageQueue mockQueue = QueueMonitorHelper.MockQueue(sendDelegate, receiveDelegate).Object;
}
public static Mock<MessageQueue> MockQueue
(Action<Message> sendDelegate, Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate)
{
Mock<MessageQueue> mockQueue = new Mock<MessageQueue>(MockBehavior.Strict);
Expression<Action<MessageQueue>> sendMock = (msmq) => msmq.Send(It.IsAny<Message>()); //message => messages.Enqueue(message);
mockQueue.Setup(sendMock).Callback<Message>(sendDelegate);
Expression<Func<MessageQueue, Message>> receiveMock = (msmq) => msmq.Receive(It.IsAny<TimeSpan>(), It.IsAny<MessageQueueTransaction>());
mockQueue.Setup(receiveMock).Returns<TimeSpan, MessageQueueTransaction>(receiveDelegate);
return mockQueue;
}