2016-08-24 22 views
0

私は以下のロガーロガークラスを持っています。MoqとAutofacを使ったユニットテスト

いくつかの所見:

  1. 私はSystem.IO依存性と依存関係を断ち切るためにインターフェイスIFileWrapperを作成するために必要な、ユーザーの依存性注入(Autofac)
  2. することができて、私はユニットテストすることができましたメモリストリングを使用してIFileWrapperを実装することによってFileWrapper.WriteLogメソッドを実装しましたが、メソッド内で期待される動作をテストしたい場合、例外がスローされたり、不正なパスやファイル名などの例外が発生したりします。

    /// <summary> 
    /// Creates an instance of type <see cref="FileLogger"/> 
    /// </summary> 
    /// <remarks>Implements the Singleton Pattern</remarks> 
    private FileLogger() 
    { 
        FileName = string.Format("\\{0: MMM dd, yy}.log", DateTime.Now); 
        Path = Environment.CurrentDirectory; 
    
        FileWrapper = ContainerBuilderFactory.Container.Resolve<IFileWrapper>(); 
    } 
    
    /// <summary> 
    /// Log the <paramref name="Message"/> in the <paramref name="Path"/> specified. 
    /// The <paramref name="UserName"/>, <paramref name="Host"/> must be supplied 
    /// </summary> 
    /// <example> 
    ///  <code> 
    ///   var handler = new LoggerHandlerFactory(); 
    ///   var logger = handler.GetHandler<FileLogger>(); 
    ///   logger.Log("Hello CSharpLogger"); 
    ///  </code> 
    /// </example> 
    /// <exception cref="ArgumentNullException"></exception> 
    /// <exception cref="ArgumentException"></exception> 
    /// <exception cref="NotSupportedException"></exception> 
    /// <exception cref="FileNotFoundException"></exception> 
    /// <exception cref="IOException"></exception> 
    /// <exception cref="SecurityException"></exception> 
    /// <exception cref="DirectoryNotFoundException"></exception> 
    /// <exception cref="UnauthorizedAccessException"></exception> 
    /// <exception cref="PathTooLongException"></exception> 
    /// <exception cref="ArgumentOutOfRangeException"></exception> 
    /// <exception cref="FormatException"></exception> 
    public void Log(string message, LogLevel level = LogLevel.INFO) 
    { 
        lock (_current) 
        { 
         var configLevel = CSharpLoggerConfiguration.Configuration.GetLogLevel(); 
    
         if (configLevel != LogLevel.OFF & level != LogLevel.OFF && configLevel >= level) 
         { 
          try 
          { 
           FileWrapper.WriteLog(string.Concat(Path, FileName), message, level); 
          } 
          catch (CSharpLoggerException) 
          { 
           throw; 
          } 
         } 
        } 
    } 
    

だから、私は部品番号を使用して、次の単体テストを作成しました:

//arrange 
     CSharpLoggerConfiguration.Configuration.SetLogLevel(LogLevel.DEBUG); 

     var mock = new Mock<IFileWrapper>(); 
     mock.Setup(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>())); 

     logger.FileWrapper = mock.Object; 

     //act 
     logger.Log("Hello CSharpLogger", LogLevel.DEBUG); 
     logger.Log("Hello CSharpLogger", LogLevel.WARN); 

     //assert 
     mock.Verify(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()), Times.Exactly(2)); 

これまでのところは良いです。私が気にしていないのは、この行です:logger.FileWrapper = mock.Object;私はFileWrapperの秘密を秘密にしておきたいと思います。

アドバイスは大歓迎です。

詳細については、http://csharplogger.codeplex.com/コードを公開します。

答えて

3

constructor injectionを使用してください。要するに;プロパティを設定してサービス(この場合はファイルラッパー)を提供する代わりに、IFileWrapper引数を取るpublicコンストラクタをロガーに作成させます。 FileWrapperFactoryがの静的インスタンスを維持するので

public static class FileWrapperFactory 
{ 
    private static IFileWrapper _fileWrapper; 

    public static IFileWrapper GetInstance() 
    { 
     return _fileWrapper ?? (_fileWrapper = CreateInstance()); 
    } 

    private static IFileWrapper CreateInstance() 
    { 
     // do all the necessary setup here 
     return new FileWrapper(); 
    } 
} 


public class StuffDoer 
{ 
    public void DoStuff() 
    { 
     var logger = new FileLogger(FileWrapperFactory.GetInstance()); 

     logger.WriteLog("Starting to do stuff..."); 

     // do stuff 

     logger.WriteLog("Stuff was done."); 
    } 
} 

public class Logger 
{ 
    public Logger(IFileWrapper fileWrapper) 
    { 
     FileWrapper = fileWrapper; 
    } 

    public IFileWrapper FileWrapper { get; } 
} 

// in your test: 
var logger = new Logger(mock.Object); 

はより徹底的にシングルトンファイルラッパーを持っていることについての質問に答えるために、ここでのアプリケーション(非テスト)のためのコードサンプルコードですファイルラッパー、あなたは1つ以上を持つことはありません。しかし、そのような複数のロガーを作成することができ、気にする必要はありません。将来、多くのファイルラッパーを用意しても問題ない場合は、ロガーコードを変更する必要はありません。

現実世界のアプリケーションでは、私はこの種類のDIフレームワークを選択して、このすべての本を手にすることをお勧めします。ほとんどの場合、シングルトンインスタンスに対する優れたサポートがあり、基本的には上記のFileWrapperFactoryの処理を行います(通常、より洗練された堅牢な方法で行われます)。FileWrapperFactoryは、たとえばスレッドセーフではありません。

+0

こんにちはトーマスを示唆します、 お返事をありがとうございます。私はそれについて考えましたが、私のクラスはシングルトンクラスなので、うまくいきませんでしたか? – user1922446

+0

このパターンを適用するためにインスタンスを挿入する必要があるため、サービスを静的クラスとして避ける必要があります。静的な(シングルトン)*インスタンスの使用は問題ありませんが、インスタンスがシングルトンであるという事実は*ロガーの責任ではありません*。 (これは、ファイルラッパーのインスタンスを提供するためにロガーをインスタンス化し、そのたびにシングルトンインスタンスを選択する責任があります。) –

+0

@ user1922446:私の意味をより深く説明するために私の更新を参照してください。 –

0

コードコメントには、ロガーがシングルトンであることが示されているため、依存関係を設定するためのコンストラクタインジェクション以外の方法が必要です。レガシーコードの彼の本では、マイクの羽が適切に命名されたこのような目的のために機能を、今、この機能がうまくいけば、異なる目的のために使用されることはありません

public void SetInstanceForTesting(IFileWrapper fileWrapper) {...} 

ような何か...

+0

こんにちはBernhard、 ご返信ありがとうございます。はい、私は抽象的なファクトリとシングルトンデザインパターンを使用していますが、依存関係注入を使用してトリッキーな作業をしています。私は何が最善のアプローチかもしれないか見てみようとしています。 – user1922446

関連する問題