Вопрос

Я хочу обработать исключение ManagementException только для определенного ErrorCode, и у меня возникают проблемы при написании модульного теста для него. Обычно я написал бы тест, чтобы он был примерно таким:

Searcher search = MockRepository.GenerateMock<Searcher>(); 
// wrapper for ManagementObjectSearcher

...

search.Expect(s => s.Get()).Throw(new ManagementException());

...

Однако, это не устанавливает ErrorCode на тот, который мне нужен, в частности, ManagementException не имеет конструктора, который устанавливает это значение.

Как это можно сделать?

(Обратите внимание, что я использую RhinoMocks в качестве моей среды для имитации, но я предполагаю, что она не зависит от платформы; все, что мне нужно знать здесь, это как создать исключение ManagementException, имеющее конкретное значение ErrorCode. Также я нашел несколько ссылок на System.Management.ManagementException.ThrowWithExtendedInfo (ManagementStatus errorCode) онлайн, но, по-видимому, он не является общедоступным).

Это было полезно?

Решение

Наименьшее усилие преодолеть это препятствие - статический вспомогательный / служебный метод, который использует отражение для взлома слота в требуемом коде ошибки. Используя самый лучший Reflector, я вижу частный " errorCode " поле, которое устанавливается только через внутренние ctors, определенные в ManagementException. Итак:)

public static class EncapsulationBreaker
   {
      public static ManagementException GetManagementExceptionWithSpecificErrorCode(ManagementStatus statusToBeStuffed)
      {
         var exception = new ManagementException();
         var fieldInfo = exception.GetType().GetField("errorCode", 
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.DeclaredOnly);
         fieldInfo.SetValue(exception, statusToBeStuffed);
         return exception;
      }
   }

Проверено, что это работает

[Test]
      public void TestGetExceptionWithSpecifiedErrorCode()
      {
         var e = EncapsulationBreaker.GetManagementExceptionWithSpecificErrorCode(ManagementStatus.BufferTooSmall);
         Assert.AreEqual(ManagementStatus.BufferTooSmall, e.ErrorCode);
      }

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

Другие советы

Получите класс из ManagementException и скройте реализацию кода ошибки с вашим собственным. Пусть ваш макет вернет этот класс.

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

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

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

Редактировать : вполне возможно, что ErrorCode просто нельзя переопределить (я ненавижу настолько жесткие языки, что они могут прекратить тестирование таким образом, но не могут отрицать, что они существуют; -). В этом случае Dependency Injection все еще может спасти вас - DI - один из моих любимых шаблонов для тестирования.

Цель DI в тестировании - отделить тестируемый код от жестких допущений, которые могут помешать тестированию - и это именно то, что мы имеем здесь, хотя и в необычной форме. Ваш тестируемый код в настоящее время, скажем, x.ErrorCode , чтобы получить код ошибки исключения x. Очень хорошо, тогда он должен выполнить вместо этого getErrorCode (x) , где getErrorCode - делегат, который обычно просто выполняет return x.ErrorCode ; и он должен иметь установщик для делегата getErrorCode , чтобы в целях тестирования вы могли изменить его на делегат, который возвращает 23 (или любое другое значение кода ошибки, которое вы хочу смоделировать для тестирования).

Детали могут различаться, но внедрение зависимостей может (среди прочего) помочь компенсировать некоторые виды чрезмерной жесткости в объектах, которые вы неизбежно получаете из системы (или из других библиотек & amp; c, которые вы не можете напрямую изменить), как в этом пример.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top