如何測試 private method

最近在做測試時,要進行 private method 的測試,由於保護層級的關係,無法直接測試,這邊記錄如何用其他方式達到 private method 的測試


為了測試,在某些 class 裡的 method 本來是定義為 private or protected 的方法,網路上有些方法是會教你改成 internal 的方式,讓你可以在 unit test 比較容易測試
 

my.Person.program.Run();

namespace my.Person
{
   using lib.Person;
   public static class program
   {
       public static void Run()
       {
           var p = new Person()
           {
               id =1
           };
           
           //會失敗
           Console.WriteLine( p.GetDBId());
       }

   }
}

namespace lib.Person
{
   public class Person
   {
      public int id {set;get;}
      
      //假設 db id 是由 id欄位去運算得知,不適合給外部物件知道的資訊
      private int GetDBId()
      {
           return id*10;
      }
   }
}

如果是用 internal 的方式,就會長成這樣,但實際上這個方法還是不適合暴露出去

public class Person
{
  public int id {set;get;}
  
  //假設 db id 是由 id欄位去運算得知,不適合給外部物件知道的資訊
  internal int GetDBId()
  {
       return id*10;
  }
}

而為了讓 lib.Person 可以讓 my.Person 可以存取,還得放上一個檔案騙編譯器

[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("my.Person")]
namespace cache
{
   internal class VisibleProject
   {
   }
}


而實質上,其實不需要把 private 的方法改成 internal,也可以在測試達到一樣的目地,前提是我們真的有必要去測 private 的方法
否則其他 public 的 method 應該也會用到 private 的方法,應當是去測公開的方法就好
比如說像 private method 可能是由事件所觸發,比方說 rabbit mq 這類型的

另一個測試的方法就是用反射的方法取到 method 後,再用 invoke 的方式去執行

my.Person.program.Run();
namespace my.Person
{
   using lib.Person;
   public static class program
   {
       public static void Run()
       {
           var p = new Person()
           {
               id =1
           };
           
           
           InvokePrivateMehtod(p, "GetDBId", null );
       }
       
       private static void InvokePrivateMehtod<T>(T instance, string methodName, object[] parameters)
       {
           MethodInfo methodInfo = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
           
           if (parameters == null)
               Console.WriteLine( methodInfo.Invoke(instance, null));
            else
               Console.WriteLine( methodInfo.Invoke(instance, parameters));
       }
   }
}
namespace lib.Person
{
   public class Person
   {
      public int id {set;get;}
      
      //假設 db id 是由 id欄位去運算得知,不適合給外部物件知道的資訊
      private int GetDBId()
      {
           return id*10;
      }
   }
}

至於 .net framework 的話,可以使用 PrivateObject 這種寫法,然而 .net core 已經棄用這個物件方法了

但底層方法看起來應當是雷同的


參考來源
https://methodpoet.com/test-private-methods/