4

Loggable基本クラス ( ILoggableを実装する)をインターセプトするインターセプターの単体テストを作成したいと考えています。
Loggable基本クラスには呼び出すメソッドがなく、ロギング機能によって初期化されるためにのみ使用されます。
私の理解では、次のことを行う必要があります。

  1. ILoggableILoggerのモック
  2. ロギング機能の初期化
  3. インターセプターを登録する
  4. モックされたILoggableのメソッドを呼び出す

問題は、ILoggableインターフェイスに呼び出すメソッドがないため、何もインターセプトされないことです。
ここで行動する正しい方法は何ですか?ILoggable
を手動で モックして、呼び出すスタブ メソッドを追加する必要がありますか? また、コンテナーもモックする必要がありますか?

Moq と NUnit を使用しています。
編集:
参照用のインターセプターの実装は次のとおりです。

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
}
4

3 に答える 3

6

Loggerクラスでプロパティを使用するのがインターセプターだけである場合、なぜそこにあるのですか? インターセプターでそれを持っているかもしれません。(Ayendeが彼の投稿で説明したように ここ)。

それ以外は - インターセプターはインターフェイスとやり取りする単なるクラスです - すべてが高度にテスト可能です。

于 2011-04-28T02:09:52.850 に答える
4

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);
     }
}
于 2011-04-28T05:46:02.553 に答える
0

メソッドはありませんか?何をテストしていますか?

個人的には、これは行き過ぎのように聞こえます。TDD とコード カバレッジが定説であることは理解していますが、メソッドを使用せずにインターフェイスをモックし、モック フレームワークが指示どおりに動作することを証明した場合、実際に何を証明したことになりますか?

ここで別の誤った方向性が起こっています: ロギングは、アスペクト指向プログラミングの「こんにちは世界」です。インターセプター/アスペクトでログインしていないのはなぜですか? そのようにした場合、すべてのクラスが実装する理由はありませんILoggable。宣言的にロギング機能でそれらを装飾できます。侵襲性の低い設計であり、インターセプターをより適切に使用していると思います。

于 2011-04-27T11:31:23.747 に答える