我可以模拟私有方法或测试此POST方法的正确方法是什么?

时间:2014-08-05 21:49:55

标签: c# asp.net-mvc unit-testing mocking moq

我有一个现有的ASP.NET MVC应用程序,想要创建一些单元测试,我很快遇到了下面的问题。有没有某种方法可以使用MOQ并说“当私人方法GETCLIENTIP运行时,然后返回' xxx'”?

因为现在它使用了HttpContext的部分,当然单元测试没有。

public HttpResponseMessage Post([FromBody]TriageCase TriageCase)
{
    if (ModelState.IsValid)
    {
        //Get the IP address from the request
        TriageCase.ipAddress = this.GetClientIP(Request);

        _log.Info("IP Address = " + TriageCase.ipAddress);
    }
}

public void Verify_Not_A_Suicide()
{
    TriageCaseRepository repository = new TriageCaseRepository();
    var controller = new TriageCasesController(repository);
    //This will not work because I must mock a private method in the controller?
    HttpResponseMessage result = controller.Post(new TriageCase());
}

3 个答案:

答案 0 :(得分:3)

以适当的TDD方式,你不进行单元测试"私人"或"内部"方法。单元测试中仅使用公共接口。如果你对一个私有/内部方法进行单元测试,那么你就会将你的单元测试与特定的实现紧密联系起来。

应该做的是使用依赖注入来注入实现"依赖"的类/接口。您需要进行单元测试的功能。这将有助于进一步模块化您的代码,从而使维护更容易。

答案 1 :(得分:1)

有几种方法可以做到这一点,从讨厌的复杂问题到简单的交易问题。这里有一些:

  • 您可以使GetClientIP方法与HttpContext无关。然后把它变成内部的。用InternalsVisibleTo标记控制器组件并放置单元测试组件路径。

使该方法与HttpContext无关,使您免于拥有HttpContextBase(从3.5开始的抽象http上下文类以启用测试)并提供模拟等等。(顺便说一下,你应该考虑一下特别是对于MVC而言,将特定字符串作为参数传递给方法。例如特定的服务器变量字符串。

  • 您可以使GetClientIP方法接收HttpContextBase作为参数,然后将其设为内部。用InternalsVisibleTo标记控制器组件并放置单元测试组件路径。

在您的控制器操作中,您需要将此方法称为this.GetClientIP(new HttpContextWrapper(HttpContext.Current))

您的单元测试可以设置可模拟的上下文。还有更多,您可以在上下文中设置是否调用Request属性,或者是否进行了与ip地址相关的服务器变量调用。 (更不用说直接IP地址值验证)

  • 您可以使用FakeItEasy或Microsoft Moles等为私有方法创建私有访问器。我通常不会这样做。

  • 您可以编写一个接口库INetworkUtility,它有一个方法可以为您提供IP地址。你的控制器可以使用这个界面。它也可以单独测试。

  • 你可以有一个公共助手类来获取ip地址,可以进行单元测试。

正如您所看到的,每个解决方案都需要做一些权衡。

从Request对象获取IP地址是一个独立的逻辑,不管mvc或web api或asp web表单。 (尽管仍然是Web特定的)因此将它作为辅助方法或基于接口的辅助方法没有害处。

个人而言,我更喜欢内部方法,(因为它几乎是私有的)并且不需要太多的代码更改。

答案 2 :(得分:0)

我一般不尝试测试私有方法。它只是变得混乱 - 测试动作的输入和输出......

要么执行依赖注入,要么考虑“测试双重”或“访问者”类......

对于 test double - 将private方法设为protected,然后继承控制器并根据需要手动模拟输入或输出。我并没有说明这是否比Ioc更好或更差,我说这是另一种方式。

protected virtual string ExecuteIpnResponse(string url)
{
  var ipnClient = new WebClient();
  var ipnResponse = ipnClient.DownloadString(url);
  return ipnResponse;
}

我最近在这个测试风格上发了一篇关于检查paypal电话的帖子。 http://viridissoftware.wordpress.com/2014/07/29/paypal-ipn-payment-notification-example-in-c/