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

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

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

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

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

10 ответов

  1. Существует ли способ реализовать запечатанный класс из интерфейса… и издеваться над интерфейсом вместо этого?

    Что-то во мне чувствует, что запечатывание классов неправильно в первую очередь, но это только я 🙂

  2. Мое общее правило заключается в том, что объекты, которые мне нужно подделать, также должны иметь общий интерфейс. Я думаю, что это правильно с точки зрения дизайна и делает тесты намного проще (и, как правило, то, что вы получаете, Если вы делаете TDD). Подробнее об этом можно прочитать в последнем посте Google Testing Blog (см. пункт 9).

    Кроме того, я работаю в основном на Java в течение последних 4 лет, и я могу сказать, что могу сосчитать на одной руке количество раз, когда я создал окончательный (запечатанный) класс. Еще одно правило здесь-у меня всегда должна быть веская причина, чтобы запечатать класс, а не запечатывать его по умолчанию.

  3. Для .NET можно использовать что-то вроде TypeMock , который использует API профилирования и позволяет подключаться к вызовам практически ко всему.

  4. Проблема с TypeMock в том, что он оправдывает плохой дизайн. Теперь, я знаю, что это часто чей-то плохой дизайн, что он скрывается, но позволяя ему в ваш процесс разработки может привести очень легко разрешить свои собственные плохие проекты.

    Я думаю, что если вы собираетесь использовать насмешливый фреймворк, вы должны использовать традиционный (например, Moq) и создать слой изоляции вокруг несъемной вещи, а вместо этого издеваться над слоем изоляции.

  5. Я обычно беру маршрут создания интерфейса и адаптера / прокси-класса, чтобы облегчить издевательство над запечатанным типом. Тем не менее, я также экспериментировал с пропуском создания интерфейса и созданием типа прокси-сервера, не запечатанного виртуальными методами. Это сработало хорошо, когда прокси-сервер действительно является естественным базовым классом, который инкапсулирует и пользователи являются частью запечатанного класса.

    При работе с кодом, который требовал этой адаптации, я устал от выполнения тех же действий по созданию интерфейса и типа прокси, поэтому я реализовал библиотеку для автоматизации задачи.

    Код несколько более сложный, чем образец, приведенный в статье, на которую вы ссылаетесь, так как он создает сборку (вместо исходного кода), позволяет генерировать код, который будет выполняться на любом типе, и не требует столько конфигурации.

    Для получения дополнительной информации, пожалуйста, обратитесь к этой странице .

  6. Я почти всегда избегаю зависимостей от внешних классов в глубине моего кода. Вместо этого, я бы предпочел использовать адаптер/мост, чтобы поговорить с ними. Таким образом, я имею дело с моей семантикой, и боль перевода изолирована в одном классе.

    Это также упрощает переключение зависимостей в долгосрочной перспективе.

  7. Вполне разумно издеваться над запечатанным классом, потому что многие классы рамок запечатаны.

    В моем случае я пытаюсь подделать класс 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;
        }
    
  8. Я считаю, что кроты из Microsoft Research позволяют вам это делать. Со страницы кроты:

    Кроты могут использоваться для обхода любой .NET
    метод, включая невиртуальный / статический
    методы в загерметизированных типах.

    Обновление: в предстоящем выпуске VS 11 есть новая платформа под названием «Fakes», которая предназначена для замены Кротов:

    Фреймворк Fakes в Visual Studio 11 является следующим поколением Кротов и заглушек и в конечном итоге заменит его. Подделки отличаются от кротов, поэтому переход от кротов к подделкам потребует некоторых изменений в коде. Руководство по этой миграции будет доступно позже.

    Требования: Visual Studio 11 Ultimate, .NET 4.5

  9. Хотя в настоящее время он доступен только в бета-версии, я думаю, что стоит иметь в виду функцию оболочки новой платформы Fakes (часть бета-версии Visual Studio 11).

    Типы оболочек предоставляют механизм обхода любого метода .NET к определяемому пользователем делегату. Типы оболочек генерируются кодом генератором подделок, и они используют делегаты, которые мы называем типами оболочек, чтобы указать новые реализации методов. Под капотом типы оболочек используют обратные вызовы, которые были введены во время выполнения в телах MSIL метода.

    Лично я смотрел на использование этого, чтобы издеваться над методами на запечатанных классах фреймворка, таких как DrawingContext.

  10. Я столкнулся с этой проблемой недавно, и после чтения / поиска в интернете, кажется, нет простого способа обойти, кроме как использовать другой инструмент, как упоминалось выше.
    Или грубое обращение с вещами, как я сделал:

    • Создайте экземпляр запечатанного класса без вызова конструктора.
    • Система.Во время выполнения.Сериализация.FormatterServices.GetUninitializedObject (instanceType);

    • Назначение значений свойствам / полям через отражение

    • YourObject.метод GetType.)(GetProperty («PropertyName»).SetValue (dto, newValue, null);
    • YourObject.метод GetType.)(GetField («FieldName»).SetValue (dto, newValue);