Exceptionが発生したメソッドの引数をメッセージ(ログ)出力する

##概要
・Exceptionが発生した時、引数をメッセージ(ログ)に出力してほしい。
・メソッドをつくる度にTry Catchを書きたくはない。
・呼び出しクラスの大本にTry Catchを仕込みたくない。
・AOPで対応しよう！

##残念なメッセージ

NUnitでAssert.IsTrue(bool result)というメソッドがあります。
この子はTrueかFalseしか知らないので気が利きません。

```csharp:TestPage.cs
string a = "吾輩は猫である";
string b = "犬";

public void TextContains(string a, string b)
{
    Assert.IsTrue( a.Contains(b) );// False：試験エラーとなる
}
```

```:残念な結果
MESSAGE:
  Expected: True
  But was:  False
```

このままでは何でエラーになったのかわかりません。

##こんなメッセージがほしい

引数の値も教えてくれると嬉しいです。

```:素敵な結果
MESSAGE:
  Expected: True
  But was:  False
=================
TextContains
a: 吾輩は猫である
b: 犬
```

##メッセージを仕込む

###C#でAOPをするため、PostSharpをインストールします。
・Visual Studio用の[PostSharp](http://visualstudiogallery.msdn.microsoft.com/a058d5d3-e654-43f8-a308-c3bdfdd0be4a)をダウンロード＆インストールします。
・NuGetで[PostSharp](https://www.nuget.org/packages/PostSharp/)を参照に追加します。

###Exception発生時に割り込みするクラスを作成します。
NUintの結果レポートにはAssertExceptionのメッセージが出力されるので、
メッセージを乗っ取って引数を出力します。

```csharp:TraceArgument.cs
[Serializable]
public sealed class TraceArgument : OnMethodBoundaryAspect
{
    // Exception時に動作
    public override void OnException(MethodExecutionArgs args)
    {
        base.OnException(args);

        // AssertExceptionのときメッセージを拡張
        if (args.Exception.GetType() == typeof(AssertionException))
            TraceMethodArguments(args);
    }

    private static void TraceMethodArguments(MethodExecutionArgs args)
    {
        // メソッドの引数
        Arguments parameters = args.Arguments;
        if (parameters == null) return;

        StringBuilder message = new StringBuilder();
        message
            .AppendLine(args.Exception.Message) // 従来のメッセージ
            .AppendLine("------------------")
            .AppendLine(args.Method.Name);  // メソッド名

        // 引数をループ
        int index = 0;
        string paramValue = null;
        foreach (object p in parameters)
        {
            // 引数の名前
            string paramName = args.Method.GetParameters()[index].Name;

            // 引数の型
            Type type = p.GetType();

            // 型ごとにValueを取り出す
            if (type == typeof(string) || type == typeof(int) || type == typeof(double) || type == typeof(decimal))
            {
                paramValue = (string)p;
            }
            else if (type.IsEnum)
            {
                paramValue = p.ToString();
            }
            else if (type == typeof(XmlDocument))
            {
                paramValue = ((XmlDocument)p).OuterXml;
            }
            else
            { //try to serialize
                try
                {
                    XmlSerializer serializer = new XmlSerializer(p.GetType());
                    StringWriter strWriter = new StringWriter();

                    serializer.Serialize(strWriter, p);
                    paramValue = strWriter.ToString();
                }
                catch
                {
                    paramValue = "[引数をシリアライズできませんでした]";
                }
            }
            message
                .Append("  ")
                .Append(paramName).Append(": ")
                .Append(paramValue).AppendLine()
                ;
            index++;
        }

        // Exceptionにメッセージを詰めなおす
        AssertionException ex = new AssertionException(message.ToString(), args.Exception);
        throw ex;
    }
}
```

```csharp:TestPage.cs
[TraceArgument]
public class WebDriverWrapper 
{
    public void TextContains(string a, string b)
    {
        Assert.IsTrue( a.Contains(b) );
    }
}
```

５．試験に失敗する(AssertException)と引数を出力してくれるようになります

```:素敵な結果
MESSAGE:
  Expected: True
  But was:  False
=================
TextContains
a: 吾輩は猫である
b: 犬
```

##思ったこと＆利用例
PostSharpが神いわゆるゴッドでした。
紙メソッドが神メソッドになりました。
OnMethodBoundaryAspectで割り込めるタイミングはOnException以外に[OnEntry, OnExit, OnSuccess](http://www.postsharp.net/aspects/method-decorator#title0)があります。

私は今回作成したクラスをSeleniumのWebDriverのラッパークラスに設定しています。
NUnitでエラーが出たときに、実際の引数でどんな値をチェックしていたのかわかるととても便利です。
Seleniumは、ローカルで成功したのにサーバで失敗したり、
時々一回だけ失敗したりするので、エラー原因を簡単に知れると便利です。

##参考
[Exception発生時にすべての変数をトレースする(StackOverflow.com)](http://stackoverflow.com/questions/362124/how-to-trace-all-local-variables-when-an-exception-occurs)