Как провести юнит-тест перехватчика? - PullRequest
4 голосов
/ 27 апреля 2011

Я хочу написать несколько модульных тестов для перехватчика, который перехватывает базовый класс Loggable (который реализует ILoggable ).
Базовый класс Loggable не имеет методов для вызова и используется только для инициализации средством ведения журнала.
Насколько я понимаю, я должен:

  1. Макет ILoggable и ILogger
  2. Инициализация средства ведения журнала
  3. Зарегистрировать мой перехватчик на нем
  4. Вызвать какой-нибудь метод из поддельного ILoggable

Проблема в том, что у моего ILoggable интерфейса нет методов для вызова, и поэтому ничего не будет перехвачено.
Как правильно действовать здесь?
Должен ли я издеваться ILoggable вручную и добавить метод-заглушку для вызова?
Кроме того, я должен издеваться над контейнером?

Я использую Moq и NUnit.
EDIT:
Вот моя реализация перехватчика для справки:

public class LoggingWithDebugInterceptor : IInterceptor
{
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        var invocationLogMessage = new InvocationLogMessage(invocation);

        ILoggable loggable = invocation.InvocationTarget as ILoggable;

        if (loggable == null)
            throw new InterceptionFailureException(invocation, string.Format("Class {0} does not implement ILoggable.", invocationLogMessage.InvocationSource));

        loggable.Logger.DebugFormat("Method {0} called with arguments {1}", invocationLogMessage.InvokedMethod, invocationLogMessage.Arguments);

        Stopwatch stopwatch = new Stopwatch();
        try
        {
            stopwatch.Start();
            invocation.Proceed();
            stopwatch.Stop();
        }
        catch (Exception e)
        {
            loggable.Logger.ErrorFormat(e, "An exception occured in {0} while calling method {1} with arguments {2}", invocationLogMessage.InvocationSource, invocationLogMessage.InvokedMethod, invocationLogMessage.Arguments);
            throw;
        }
        finally
        {
            loggable.Logger.DebugFormat("Method {0} returned with value {1} and took exactly {2} to run.", invocationLogMessage.InvokedMethod, invocation.ReturnValue, stopwatch.Elapsed);
        }
    }

    #endregion IInterceptor Members
}

Ответы [ 3 ]

6 голосов
/ 28 апреля 2011

Если это просто перехватчик, который использует свойство Logger в вашем классе, то зачем вообще его использовать? Вы могли бы также иметь это на перехватчике. (как объяснил Айенде в своем посте здесь ).

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

4 голосов
/ 28 апреля 2011

Я согласен с Krzysztof, если вы хотите добавить ведение журнала через AOP, ответственность и детали реализации ведения журнала должны быть прозрачны для вызывающей стороны.Таким образом, это то, что перехватчик может владеть.Я попытаюсь обрисовать, как я это проверил бы.

Если я правильно следую за вопросом, ваш ILoggable на самом деле является просто контейнером именования для аннотирования класса, чтобы перехватчик мог определить, должен ли он выполнять регистрацию.Он предоставляет свойство, которое содержит Logger.(Недостатком этого является то, что класс все еще должен настраивать Logger.)

public interface ILoggable
{
     ILogger { get; set; }
}

Тестирование перехватчика должно быть простым процессом.Единственная проблема, которую я вижу, которую вы представили, состоит в том, как вручную создать входной параметр IInvocation , чтобы он напоминал данные времени выполнения.Вместо того, чтобы пытаться воспроизвести это с помощью насмешек и т. Д., Я бы посоветовал вам протестировать его с помощью классической проверки на основе состояния: создайте прокси-сервер, который использует ваш перехватчик, и убедитесь, что ваш журнал отражает то, что вы ожидаете.

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

public class TypeThatSupportsLogging : ILoggable
{
     public ILogger { get; set; }

     public virtual void MethodToIntercept()
     {
     }

     public void MethodWithoutLogging()
     {
     }
}

public class TestLogger : ILogger
{
     private StringBuilder _output;

     public TestLogger()
     {
        _output = new StringBuilder();
     }

     public void DebugFormat(string message, params object[] args)
     {
        _output.AppendFormat(message, args);
     }

     public string Output
     {
        get { return _output.ToString(); }
     }
}

[TestFixture]
public class LoggingWithDebugInterceptorTests
{
     protected TypeThatSupportsLogging Input;
     protected LoggingWithDebugInterceptor Subject;
     protected ILogger Log;         

     [Setup]
     public void Setup()
     {
         // create your interceptor
         Subject = new LoggingWithDebugInterceptor();

         // create your proxy
         var generator = new Castle.DynamicProxy.ProxyGenerator();
         Input = generator.CreateClassProxy<TypeThatSupportLogging>( Subject );

         // setup the logger
         Log = new TestLogger();
         Input.Logger = Log;
     }

     [Test]
     public void DemonstrateThatTheInterceptorLogsInformationAboutVirtualMethods()
     {
          // act
          Input.MethodToIntercept();

          // assert
          StringAssert.Contains("MethodToIntercept", Log.Output);
     }

     [Test]
     public void DemonstrateNonVirtualMethodsAreNotLogged()
     {
          // act
          Input.MethodWithoutLogging();

          // assert
          Assert.AreEqual(String.Empty, Log.Output);
     }
}
0 голосов
/ 27 апреля 2011

Нет методов? Что ты тестируешь?

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

Здесь происходит еще одно неправильное направление: регистрация - это «привет мир» аспектно-ориентированного программирования. Почему вы не делаете вход в перехватчик / аспект? Если бы вы сделали это таким образом, у всех ваших классов не было бы причин для реализации ILoggable; Вы можете украсить их с возможностью ведения журнала декларативно. Я думаю, что это менее агрессивный дизайн и лучшее использование перехватчиков.

...