Насмешливые запечатанные занятия могут быть довольно болезненными. В настоящее время я предпочитаю шаблон адаптера, чтобы справиться с этим, но что-то в этом просто кажется странным.
Итак, как лучше всего издеваться над закрытыми уроками?
Ответы Java приветствуются . На самом деле, я ожидаю, что сообщество Java занимается этим дольше и может многое предложить.
Но вот некоторые из мнений .NET:
Мое общее правило таково, что объекты, которые мне нужно смоделировать, также должны иметь общий интерфейс. Я думаю, что это правильно с точки зрения дизайна и делает тестирование намного проще (и обычно это то, что вы получаете, если делаете TDD). Подробнее об этом можно прочитать в последнем посте блога тестирования Google (см. Пункт 9).
Кроме того, я работал в основном на Java последние 4 года и могу сказать, что могу рассчитывать с одной стороны, сколько раз я создал окончательный (запечатанный) класс. Другое правило: у меня всегда должна быть веская причина запечатать класс, а не запечатывать его по умолчанию.
sealed
ключевым словом. Есть ли способ реализовать запечатанный класс из интерфейса ... и вместо этого смоделировать интерфейс?
Что-то во мне чувствует, что запечатывание уроков в первую очередь неправильно, но это только я :)
Для .NET вы могли бы использовать что-то вроде TypeMock , который использует API профилирования и позволяет подключаться к вызовам практически ко всему.
Проблема с TypeMock в том, что он оправдывает плохой дизайн. Теперь я знаю, что зачастую скрывается чей-то плохой дизайн, но его включение в процесс разработки может очень легко привести к разрешению ваших собственных плохих проектов.
Я думаю, что если вы собираетесь использовать фальшивый фреймворк, вы должны использовать традиционный (например, Moq) и создать слой изоляции вокруг немодируемой вещи, а вместо этого смоделировать слой изоляции.
Я обычно выбираю интерфейс и класс адаптера / прокси, чтобы облегчить моделирование закрытого типа. Тем не менее, я также экспериментировал с пропуском создания интерфейса и созданием типа прокси незапечатанным с помощью виртуальных методов. Это хорошо работало, когда прокси действительно естественный базовый класс, который инкапсулирует и является частью запечатанного класса.
Когда я работал с кодом, который требовал такой адаптации, мне надоело выполнять одни и те же действия для создания интерфейса и типа прокси, поэтому я реализовал библиотеку для автоматизации задачи.
Код несколько сложнее, чем образец, приведенный в статье, на которую вы ссылаетесь, поскольку он создает сборку (вместо исходного кода), позволяет выполнять генерацию кода для любого типа и не требует такой большой конфигурации.
Для получения дополнительной информации, пожалуйста, обратитесь к этой странице .
Я почти всегда избегаю зависимостей от внешних классов в глубине моего кода. Вместо этого я бы предпочел использовать адаптер / мост, чтобы поговорить с ними. Таким образом, я имею дело со своей семантикой, и боль от перевода изолирована в одном классе.
Это также облегчает переключение моих зависимостей в долгосрочной перспективе.
Совершенно разумно издеваться над запечатанным классом, потому что многие базовые классы запечатаны.
В моем случае я пытаюсь смоделировать класс .Net MessageQueue, чтобы я мог использовать свою изящную логику обработки исключений.
Если у кого-то есть идеи о том, как преодолеть ошибку 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;
}
Я считаю, что Moles из Microsoft Research позволяет вам сделать это. Со страницы Кротов:
Кроты могут использоваться для обхода любого метода .NET, включая не виртуальные / статические методы в закрытых типах.
ОБНОВЛЕНИЕ: в следующей версии VS 11 появилась новая платформа под названием «Fakes», предназначенная для замены Moles:
Подделки Framework в Visual Studio 11 является следующим поколением родинок и окурки, и в конечном итоге заменить его. Однако Fakes отличается от Moles, поэтому переход от Moles к Fakes потребует некоторых изменений в вашем коде. Руководство по этой миграции будет доступно позже.
Требования : Visual Studio 11 Ultimate, .NET 4.5
Хотя его в настоящее время доступна только в бета - версии, я думаю , что его стоящее , имея в виду прокладка особенность новой структуры Подделки (часть Visual Studio 11 бета - версии).
Типы Shim предоставляют механизм для обхода любого метода .NET для определенного пользователем делегата. Типы Shim генерируются кодом генератором Fakes, и они используют делегаты, которые мы называем типами shim, для определения реализаций нового метода. Под капотом типы прокладок используют обратные вызовы, которые были введены во время выполнения в теле метода MSIL.
Лично я смотрел на использование этого, чтобы насмехаться над методами на запечатанных классах структуры, таких как DrawingContext.
Я столкнулся с этой проблемой недавно, и после чтения / поиска в Интернете, кажется, что нет простого пути, кроме как использовать другой инструмент, как упомянуто выше. Или грубая обработка вещей, как я сделал:
- Создайте экземпляр закрытого класса без вызова конструктора.
System.Runtime.Serialization.FormatterServices.GetUninitializedObject (instanceType);
Присвойте значения вашим свойствам / полям через отражение
- YourObject.GetType (). GetProperty ("PropertyName"). SetValue (dto, newValue, null);
- YourObject.GetType (). GetField ("FieldName"). SetValue (dto, newValue);