【问题标题】:.NET Core unit test how to Mock HttpContext RemoteIpAddress?.NET Core 单元测试如何模拟 HttpContext RemoteIpAddress?
【发布时间】:2020-03-17 13:55:36
【问题描述】:

我有一个使用 Identity 的 .NET Core 3.1 项目。对于Login 页面处理程序,我添加了一行代码,在用户登录后,它会根据用户的 IP 地址更新用户位置:

_locationRepository.UpdateUserLocationAsync(HttpContext.Connection.RemoteIpAddress);

完整代码

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _locationRepo.UpdateUserLocation(HttpContext.Connection.RemoteIpAddress);
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

我的问题是在编写单元测试时,我不知道如何正确模拟HttpContext。无论我尝试了什么,我都会不断收到空引用异常。

var httpContext = new Mock<HttpContext>();
httpContext.Setup(x => x.Connection.RemoteIpAddress).Returns(new IPAddress(16885952));

如何模拟RemoteIpAddress

【问题讨论】:

  • 展示如何将上下文分配给被测主题(PageModel)
  • 你从哪里得到异常?
  • 在控制器中模拟 HttpContext 并非易事,因为它是一个只读属性。 Here 您可以阅读原因。您最好的选择可能是从控制器中抽象出上下文属性。
  • 我使用的是剃须刀页面,而不是控制器。这会让事情变得更容易吗?
  • 其实有!请查看 MS 文档 (docs.microsoft.com/en-us/aspnet/core/test/…) 和示例 (github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/test/…) 我的错,没有注意到它的 RazorPages 应用程序。

标签: c# unit-testing asp.net-core asp.net-identity


【解决方案1】:

Xunit + FakeItEasy

            var mockHttpContextAccessor = new Fake<IHttpContextAccessor>();
            var httpContext = new DefaultHttpContext()
            {
                Connection =
                {
                    RemoteIpAddress = new IPAddress(16885952)
                }
            };
            mockHttpContextAccessor.CallsTo(x => x.HttpContext)
                .Returns(httpContext);
            var extractedIpAddress = IpExtractor.GetIpAddress(httpContext);
            //assert 192.168.1.1

【讨论】:

    【解决方案2】:

    您的设置与任何呼叫都不匹配。您还需要模拟连接:

    var connectionMock = new Mock<Microsoft.AspNetCore.Http.ConnectionInfo>(MockBehavior.Strict);
    connectionMock.SetupGet(c => c.RemoteIpAddress).Returns(new IPAddress(16885952));
    
    var httpContext = new Mock<HttpContext>(MockBehavior.Strict);
    httpContext.SetupGet(x => x.Connection).Returns(connectionMock.Object);
    

    将您的 MockBehavior 设置为 Strict,以便在调用未正确设置的属性或方法时引发异常。

    【讨论】:

      【解决方案3】:

      这是我的解决方案

      public static void MockIpAddress(this ControllerBase controller) 
          {
              controller.ControllerContext = new ControllerContext()
              {
                  HttpContext = new DefaultHttpContext()
                  {
                      Connection =
                      {
                          RemoteIpAddress = new System.Net.IPAddress(16885952)
                      }
                  }
              };
          }
      

      【讨论】:

        猜你喜欢
        • 2017-04-14
        • 1970-01-01
        • 1970-01-01
        • 2017-05-14
        • 2022-01-09
        • 1970-01-01
        • 2013-06-13
        相关资源
        最近更新 更多