PrivateObjectクラスってすげー。MSTestって地味に頑張ってるんだなぁと思った。
やりたかったこと。とあるクラスのPrivateメソッドをテストしたかった。
MSTestの場合、PrivateObjectという大変使える?クラスがいたので、それを使ってテスト対象クラスインスタンスからInvokeすれば解決してたんだよね。
まぁ、これに同等の機能クラスが実は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