2008-08-09 13 views
55

Mocking sealed classes может быть довольно больно. Я в настоящее время пользуюсь Adapter pattern, чтобы справиться с этим, но что-то вроде просто держится странно.Как вы издеваетесь над Запечатанным классом?

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

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

Но вот некоторые из .NET Мнение

+0

Не издевайтесь! Использовать композицию вместо этого (http://stackoverflow.com/a/891820/274502). – cregox 2013-09-04 19:59:18

ответ

6

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

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

+19

Я бы сказал, что у вас должна быть хорошая причина * не * запечатывать класс. Если оставить класс открытым, вам нужно подумать о том, как он будет использоваться унаследователями, что открывает множество решений обо всем коде в классе (виртуальность, защищенные свойства или частные переменные-члены и т. Д.). Это * hard * правильно спроектировать класс для наследования. Вы не должны расширять класс только ради расширения; вывод должен означать нечто специфическое для моделируемой модели. В противном случае, предпочитайте композицию. – 2009-05-26 14:46:46

+2

Брайан - так как бы вы издевались над запечатанным классом? Я согласен с вами с теоретической точки зрения, но если вы напишете что-то, зависящее от вашего закрытого класса, ваши тесты также будут зависеть от запечатанного класса. – 2009-11-22 07:48:09

+4

@abyx: К сожалению, выбор разработчика не всегда зависит от того, закрыт ли класс или нет. Возьмите, например, System.Web.HttpServerUtility в ASP.NET ... – chiccodoro 2011-07-07 13:39:39

1

Есть ли способ реализовать запечатанный класс от интерфейса ... и вместо этого имитирует интерфейс?

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

+2

Я думаю, что закрытые классы великолепны ... проблема в тестировании. Он воняет, что мы должны изменить весь наш дизайн из-за ограничений в тестировании .NET-проектов. Во многих случаях Д.И. это просто способ обойти тот факт, что статические классы/методы трудно тестировать. – 2009-11-22 07:53:40

18

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

4

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

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

1

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

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

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

Для получения дополнительной информации см. this page.

4

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

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

1

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

В моем случае я пытаюсь высмеять класс MessageQueue .Net, чтобы я мог TDD использовать мою грациозную логику обработки исключений.

Если у кого-то есть идеи о том, как преодолеть ошибку Мока относительно «Недействительной настройки для неперехваченного члена», пожалуйста, дайте мне знать.

код:

[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; 
    } 
13

Я считаю, что Moles от Microsoft Research, позволяет сделать это. На странице Moles:

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

UPDATE: есть новая структура под названием «Подделка» в предстоящем выпуске VS 11, который предназначен для замены Родинки:

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

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

1

Хотя его в настоящее время доступна только в бета-версии, я думаю, что его полезным иметь в виду shim особенность нового Fakes framework (часть Visual Studio 11 Бета-релиз).

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

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

2

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

  • Создать экземпляр закрытого класса без вызова конструктора.
  • System.Runtime.Serialization.FormatterServices.GetUninitializedObject (instanceType);

  • Присвоить значения для ваших свойств/полей с помощью отражения

  • YourObject.GetType() GetProperty ("PropertyName") SetValue (DTO, новое_значение, нуль)..;
  • YourObject.GetType(). GetField ("FieldName"). SetValue (dto, newValue);