如何在不支持"PrivateObject"的.Net Core应用程序中对私有方法进行单元测试

4

我的应用程序是一个 .NET Core 应用程序。

我有一个 public 方法,如下所示,它有两个私有方法。

   public bool CallService(JObject requestJsonObj, out Status status)
   {
        bool provisioningSuccess = false;
        var preProcessSuccess = PreProcessing(requestJsonObj,out Status status);
        var postProcessSuccess = PostProcessing(requestJsonObj,out Status status);

        if(preProcessSuccess && postProcessSuccess)
        {
              status = Status.ProvisionSuccess;
              provisioningSuccess = true;
        }
        return provisioningSuccess;
   }

这里是状态和私有类

   public enum Status
   {
       [Description("User/Pwd not valid")]
       CredentialInvalid = 111,
       [Description("Provision is success")]
       ProvisionSuccess = 112,
   }

    private PreProcessing(JObject JosnObj, 
        out Status status)
    {
           using (var client = new HttpClient())
           {
                var request = new {.........};
                var response = client.PostAsJsonAsync("api/preprocess", request).Result;
           }
    }

    private PostProcessing(JObject JosnObj, 
        out Status status)
    {
            //..... 
    }

尝试了以下方法:
     PrivateObject privateHelperObject = new PrivateObject(typeof(MainService));
     actual = (bool)privateHelperObject.Invoke("CallService", requestJsonObj,status);    

它表示

类型或命名空间名称“PrivateObject”无法找到(是否缺少使用指令或程序集引用?)

这是一个.NET CORE项目。我不确定 PrivateObject 是否受支持 .net core?


2
您不需要专门对私有方法进行单元测试。您可以编写公共方法的单元测试,以覆盖私有方法代码涵盖的所有用例。这将确保在所有情况下都测试了私有方法代码的行为。 - Chetan
4
使用普通的反射?在底层,PrivateObject并没有做出任何不同的操作。 - MakePeaceGreatAgain
为什么要对私有方法进行单元测试?因为测试公共方法将覆盖私有方法。 - Vijayanath Viswanathan
@VijayanathViswanathan 私有方法具有复杂的逻辑,例如调用API、验证响应、获取证书等等... 内部调用多个私有方法。 - kudlatiger
@Pumkko PrivateObjectExtensions 似乎是一个有趣的项目,但它只能访问属性和字段,而不能访问方法。 - C.M.
显示剩余4条评论
5个回答

7

首先,您根本不需要使用PrivateObject,因为您要测试的成员是public的:

var target = new MainService();
var actual = target.CallService(requestJsonObj, status);

即使您的方法本身调用私有方法,也不会改变您测试公共方法的方式。

如果您确实需要测试私有方法,情况会更加困难。因此,让我们使用反射来完成这一点,这也是PrivateObject在幕后执行的操作。

var mainServiceObject = new MainService();
var method = mainService.GetType().GetMethod("PreProcessing", BindingFlags.Instance | BindingFlags.NonPublic);

var result = (bool) method.Invoke(mainServiceObject, new[] { requestJsonObj, status });

然而,请注意单元测试私有成员通常被认为是代码异味,而且通常表明设计存在问题 - 即你的类正在做太多的事情,应该将其拆分成多个类,每个类都具有单一职责。


你能帮我解决一个问题吗?我想在不使用反射的情况下进行测试,我正在使用NSubstitute。我可以使用NSubstitute的“Received()”功能吗? - kudlatiger
我不明白那应该如何帮助你。我甚至不确定你实际想要达到什么目的。你想要模拟私有方法去做其他事情吗?例如,不执行网络请求而只返回静态消息?还是你想要测试私有方法? - MakePeaceGreatAgain
我想确认测试公共方法是否包含了所有私有调用。(确保它能到达 client.PostAsJsonAsync 这一行。所以我正在尝试使用“received()”特性。) - kudlatiger
如果您真的需要这样做,那么您应该重新设计您的类,因为这是不可能的。您不能模拟私有非虚方法,无论是使用反射还是PrivateObject 或其他任何方式。即使您可以 - 也许有一些花哨的库可以实现这个 - 您将会mock您的单元测试 - 这意味着你的目标类,这不是好的。所以我认为您应该从私有方法中提取一个新类,并对其进行测试。 - MakePeaceGreatAgain

6
首先,在单元测试中需要测试私有方法是很少见的情况。通常,您测试一个公共方法来调用所有其他私有(或非私有)方法。但确实有时候私有方法内部的逻辑太多,或者方法是事件/消息处理程序,无法直接调用。
其次,正如已经提到的,PrivateObject 不是 .NET Core / Standard 的一部分,而且我认为它永远都不会成为一部分。
作为解决方法,您可以使用反射在单元测试中调用私有方法。
var mainServiceObject = new MainService();
var method = typeof(MainService).GetMethod("PrivateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
var result = method?.Invoke(mainServiceObject, new object[] { requestJsonObj, status });

请注意,在您的代码中,CallService 是公共方法,因此不需要通过反射调用它。

P.s.


PreProcessing和postProcessing是私有方法。 - kudlatiger
1
然后: var method = typeof(MainService).GetMethod("PreProcessing ", BindingFlags.Instance | BindingFlags.NonPublic);var result = method?.Invoke(mainServiceObject, new object[] { requestJsonObj, status }); - Alexander Goldabin

3
var mainService = new MainService(); // This is only to visualise that you need to pass an instance of the class


var dynMethod = typeof(MainService).GetMethod("PreProcessing", BindingFlags.NonPublic | BindingFlags.Instance);

dynMethod.Invoke(mainService , new object[] { methodParams });

// methodParams应该是此方法接受的参数


1

0
如果将私有方法的作用域更改为受保护的是可以接受的,那么您可以使您的测试类继承拥有该私有方法的类。然后测试类就能够调用它并对其进行单元测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接