PrivateObjectクラスってすげー。MSTestって地味に頑張ってるんだなぁと思った。
やりたかったこと。とあるクラスのPrivateメソッドをテストしたかった。
MSTestの場合、PrivateObjectという大変使える?クラスがいたので、それを使ってテスト対象クラスインスタンスからInvokeすれば解決してたんだよね。
https://msdn.microsoft.com/ja-jp/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx
まぁ、これに同等の機能クラスが実はNUnit3.xに存在しないってことを知ってしまったわけだ。
なので、作ったw
internal static class CommonModule
{
static public Object PrivateMethodInvoke(Object testClass, String methodName, Object[] parameters)
{
if (string.IsNullOrWhiteSpace(methodName))
Assert.Fail("No Name");
MethodInfo method = testClass.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
Assert.Fail(string.Format("{0} is not found", methodName));
try
{
return method.Invoke(testClass, parameters);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
}
例外についても今まで通りの形でテストしたかったんで、ちょいテク。Try-Catch でInvoke例外とってその原因の例外を実行時例外に代替。ILコスト? まーいいんじゃない? テストコードだし、実行じゃないしw
で、テスト用にサンプルクラス作る。Privateメソッドでパラメータの文字列を数字にして「倍返し」。
public class ForPrivate
{
private int TwoTimeString(String BaseNum)
{
if (string.IsNullOrWhiteSpace(BaseNum))
throw new InvalidCastException();
int ret = int.MinValue;
if (int.TryParse(BaseNum, out ret))
{
return ret * 2;
}
throw new InvalidCastException();
}
}
そして、テストコードを書いてみる。正常ケースと例外ケースを。
public class ForPrivateTest
{
[Test, Sequential]
public void TwoTimeStringTest([Values("1", "4", "88", "-4", "0")] String prm1,
[Values(2, 8, 176, -8, 0)] int Exp1)
{
ForPrivate forPrivate = new ForPrivate();
Assert.NotNull(forPrivate);
int act = (int)CommonModule.PrivateMethodInvoke(forPrivate, "TwoTimeString", new object[] { prm1 });
Assert.AreEqual(Exp1, act);
}
[Test, Sequential]
public void TwoTimeStringExceptionTest([Values("", " ", "0.00004", null)] String prm1)
{
ForPrivate forPrivate = new ForPrivate();
Assert.Throws<InvalidCastException>(() => CommonModule.PrivateMethodInvoke(forPrivate, "TwoTimeString", new object[] { prm1 }));
}
}
実行してみた。
グリーンだよv