Как высмеивать запечатанный класс?

Издевательство над закрытыми классами может быть довольно сложной задачей. В настоящее время я предпочитаю шаблон адаптера, чтобы справиться с этим, но что-то в этом остается странным.

Итак, как лучше всего высмеивать закрытые классы?

Ответы на Java более чем приветствуются . Фактически, я ожидал, что сообщество Java занимается этим дольше и может многое предложить.

Но вот некоторые из мнений .NET:

Ответов (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;
    }