[C#・単体テスト] Shim を利用したアセンブリからの分離

単体テストメソッド作成時に Shim を利用するとメソッドのふるまいを変更したり、.NET アセンブリの呼び出しを変更したりできる。

shim を使用して単体テストでアプリケーションを他のアセンブリから分離する -msdn
http://msdn.microsoft.com/ja-jp/library/hh549176.aspx#AddFakes


サンプルを作成してみて気を付けなければならない(自分がはまった)のは、参照設定の System.dll を右クリックして 「Fakes アセンブリに追加」 を行うことだ。
Exception を判定したければ、catch をする必要があるのではないかと思う(私はテスト実装に慣れていないので他の方法があるかもしれないが)。
なお、shim はメソッドを書き換えるため、動作速度は遅くなる。

テスト対象クラス
namespace Sample
{
    public static class Y2KChecker
    {
        public static void Check()
        {
            if (DateTime.Now == new DateTime(2000, 1, 1))
                throw new ApplicationException("y2kbug!");
        }
    }
}
テストメソッド
[TestMethod]
public void Y2KChecker_発生する()
{
    using (ShimsContext.Create())
    {
System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(2000, 1, 1); };

try
{
    Y2KChecker.Check();
    Assert.Fail();
}
catch (ApplicationException)
{   
}
    }
}

[TestMethod]
public void Y2KChecker_発生しない()
{
   using (ShimsContext.Create())
   {
  System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(2001, 1, 1); };
  try
  {
     Y2KChecker.Check();
  }
  catch (ApplicationException ex)
  {
      Assert.Fail(ex.Message);
  }
  }
}

[VS2013] Private メソッドへの単体テスト実装

単体テストプロジェクトで Private メソッドへのテストを書きたい場合、方法はいくつかあるようだが、外部からアクセスできる public メソッドを書いてしまうのが手っ取り早い。
  1. クラスそのものは public である必要がある。
  2. #if DEBUG を書いておけば、デバッグ時のみ有効化される。

テスト対象クラス
using System;

namespace Sample
{
    public static class Program
    {
        static void Main(string[] args)
        {
        }
#if DEBUG
        public static string UtGetCodeName(string name) { return GetCodeName(name); }
#endif
        static string GetCodeName(string name)
        {
            if (name.Length == 0)
            {
                return null;
            }
            var r = new Random();
            return "C" + r.Next(10, 99).ToString() + name.Substring(3);
        }
    }
}
テストメソッド
[TestMethod]
public void GetCodeName_nullではないこと()
{
    Assert.IsNotNull(Sample.Program.UtGetCodeName("Daniel"));    
}
[TestMethod]
public void GetCodeName_nullになること()
{
    Assert.IsNull(Sample.Program.UtGetCodeName(""));
}

[C#] イベントログをラムダ式で検索

イベントログを抽出したい場合、foreach ですべて列挙すると時間がかかってしまう。
これをラムダ式で検索したい場合は、以下のように行う。
// 一週間のうち、ソースが SecurityCenter であるイベントログを取得。
// 戻り値は IEnumerable となる。
var period = DateTime.Now.AddDays(-7);
var logName = "Application";
var machineName = ".";
if (EventLog.Exists(logName, machineName))
{
    var log = new EventLog(logName, machineName);
    var ret = log.Entries.Cast().Where(m => m.TimeGenerated >= period && m.Source == "SecurityCenter");
    log.Close();
}
なお、TimeGenerated には少々癖があり、必ずしもイベントログに書込まれた時間ではないらしい。

EventLogEntry.TimeGenerated プロパティ - msdn
http://msdn.microsoft.com/ja-jp/library/system.diagnostics.eventlogentry.timegenerated%28v=vs.110%29.aspx

[C#] フォルダ内でパターンに一致しないファイルを削除

C# におけるフォルダ内ファイルの列挙は GetFiles() メソッドが用意されていたが、これは探索に時間がかかってしまう場合があり効率が悪いものであった。
そこで .NET Framework 4 からは EnumerateFiles() メソッドが追加された。

ファイルやディレクトリの一覧を効率的に取得するには?[4以降、C#、VB]
http://www.atmarkit.co.jp/fdotnet/dotnettips/1018enumfiles/enumfiles.html

パターンに一致するファイルはこれですぐに探せるのだが、例えば、パターンに一致しないファイルを消したい場合はもう一工夫する。
以下は D:\test 以下にある、本日日付以外のファイルをすべて削除する場合のサンプル。
var today = DateTime.Now.ToString("yyyy-MM-dd");
// .txt ファイルのみ削除対象にする場合は、EnumerateFiles の第二引数にパターンをセットする。
// foreach (var path in Directory.EnumerateFiles(@"D:\test", "*.txt").Where(x => !x.Contains(today)))
foreach (var path in Directory.EnumerateFiles(@"D:\test").Where(x => !x.Contains(today)))
{
   var fileInfo = new FileInfo(path);
   fileInfo.Delete();
}
Directory.EnumerateFiles メソッド - msdn
http://msdn.microsoft.com/ja-jp/library/vstudio/system.io.directory.enumeratefiles%28v=vs.100%29.aspx

[Windows8.1] ライブラリの表示

Windows 8.1 のエクスプローラ-の初期設定では、ライブラリが表示されていないため、表示させたい場合は以下の手順を行う。
  1. [ 表示 ] メニューの [ ナビゲーションウィンドウ ] をクリックする。
  2. [ ライブラリの表示 ] をクリック。

[コマンドプロンプト] 貼り付けのショートカットキー

コマンドプロンプトで、貼り付けをキーボードから行いたい場合は、以下の通りである。
  1. 「Alt + Space」でメニュー画面を出す。
  2. 編集 (E)
  3. 貼り付け (P)
ちなみに、コピーも「Ctrl + C」ではなく、Enter キーで行う。