2

データを取得する MVC コントローラーで使用されるインターフェイスがあります。シンプルにするために、これまでのインターフェイスは次のようになります。

public interface IDataProvider
{
    DataModel GetData();
}

アクションで呼び出されるこのインターフェイスの適切な単体テストがあります。ただし、実際の実装では、これはもちろん例外をスローする可能性のある Web サービスを呼び出します。そのため、エラーが発生した場合にメッセージをログに記録するようにテストを記述したいと考えています。

これを行うために、実際にはILoggerと呼ばれるNLogへのインターフェースであるロガーインターフェースがあります。私はこれを行うことができます:

public interface IDataProvider
{
    DataModel GetData(ILogger logger);
}

これにより、ロガーの単体テストを実行して、素晴らしくシンプルにすることができます。ただし、ロガーはこの方法とはまったく関係がないため、これが正しい方法だとは思いません。また、ロギングが必要なこのインターフェースに他のメソッドを追加し始めると、それらすべてのメソッドのパラメーターにもロガーを含める必要があります。

今考えられる最善の方法は、実装のコンストラクターにロガーを含めることです。これは次のようになります。

public class DataProvider : IDataProvider
{
    private readonly ILogger _logger;

    public DataProvider(ILogger logger)
    {
        _logger = logger;
    }

    public DataModel GetData()
    {
        // CODE GOES HERE
    }
}

ただし、これは、単体テストでロガーをテストできないことを意味します。ロガーをメソッドから分離してテスト可能にするために、これを達成する最良の方法は何ですか?

どんな助けにも感謝します、ありがとう。

編集:

ここでユニットテストコードを見逃したことに気づきました。

現時点では、GetData が次のようにアクションで呼び出されるようにしています。

var controller = new DataController(_dataProvider.Object);

controller.Index();

_dataProvider.Verify(dataProvider => dataProvider.GetData());

私がやりたいことは同じですが、ロガーの場合ですが、次のように例外がスローされた場合のみです:

_dataProvider.Setup(dataProvider => dataProvider.GetData()).Throws<WebException>();

var controller = new DataController(_dataProvider.Object);

controller.Index();

_logger.Verify(logger => logger.ErrorException(It.IsAny<string>(), It.IsAny<Exception>());

明らかに、ロガーはセットアップでデータプロバイダーに提供されます。それがもう少し理にかなっていることを願っています。

4

4 に答える 4

6

factory パターンを使用してみることができます。

ここで何が起こるかは、本番コードで発生します。 Factoryからロガーを取得しています。このファクトリでは、実際のロガー、または単体テストでセットアップされた偽のロガーのいずれかを返します。実動コードにとって、それは何の違いもありません。

単体テストでは、 Moqを使用して作成された偽のロガーを使用しています。このフェイクを使用すると、インターフェイス メソッドが呼び出されたことをテストできます。この場合はILogger.Log(). これは.Verifyメソッドを使用して行われます。

次のようなことを試してください:

ILogger.cs

public interface ILogger
{
    void Log(string message);
}

LoggerFactory.cs

public static class LoggerFactory
{
    public static ILogger Logger
    {
        get
        {
            return LoggerFactory._logger == null ? new Logger() : LoggerFactory._logger;
        }
        set
        {
            LoggerFactory._logger = value;
        }
    }
    private static ILogger _logger = null;
}

DataProvider.cs

public void GetData()
{
    var logger = LoggerFactory.Logger;

    logger.Log("..."); // etc...
}

UnitTest.cs

private void Mock<ILogger> _mockLogger = null;

public void Load()
{
    this._mockLogger = new Mock<ILogger>();

    LoggerFactory.Logger = _mockLogger.Object;
}

public void UnitTest()
{
    // test as required

    this._mockLogger.Verify(m => m.Log(It.IsAny<string>()));
}
于 2013-03-27T16:14:11.317 に答える
1

ロガーが呼び出されていることをテストする必要がある場合は、「スパイ」と呼ばれるテスト ダブルを使用することをお勧めします。これはログを記録しませんが、どのメソッド (存在する場合) が呼び出されたかを追跡します。次に、ロガーが特定のインスタンスで呼び出されることを確認できます。

これは、モック フレームワークを使用して double (またはモック) を作成することで実行できます。ILoggerまたは、実装を自分で作成することもできます。例えば:

class LoggerSpy : ILogger
{
    public string LogWasCalled;

    public void Log(string message)
    {
        LogWasCalled = true;;
    }
}

Moq を使用して ILogger をモックする例を次に示します。 Moq を使用して ILogger / ILoggerService をモックする方法

于 2013-03-27T16:11:04.603 に答える
1

モッキング フレームワーク (Moq や RhinoMocks など) を使用して、ロガーが呼び出されたことを確認します。次に、ロガーがコンストラクターを介して渡される、投稿する最終的なコード ブロックが機能します。

于 2013-03-27T16:11:08.283 に答える
1

コンストラクターでロガー (またはその他の依存関係) を渡すことは非常に標準的な方法であり、必要に応じて依存関係注入フレームワークを使用できます。

単体テストの制限としてコンストラクターでロガーを渡すのを見る理由がわかりません:個別にテストできる3つのコンポーネントがあります

  • コントローラー (提供されるデータに依存し、この依存関係をモックしてテストします)、
  • データ プロバイダー (ロギングおよび Web サービスを呼び出すことができるその他のクラスに依存します。すべての依存関係をモックして、ロギングがいつ呼び出されたかを把握し、Web サービスを呼び出す必要がないようにします)
  • logging - 何に依存しているかはわかりませんが、個別にテストできるはずです。

ノート:

  • テストにはモッキング フレームワーク (つまりmoq ) を使用します。インターフェースの実装 (例外を含む) を非常に簡単に提供できます。
  • 依存性注入フレームワーク (つまりUnity ) が機能するかどうかを確認してください。MVC4 はそれに非常に適しています。
于 2013-03-27T16:17:46.360 に答える