.NET Core单元测试如何模拟HttpContext RemoteIpAddress?

8

我有一个使用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));

我该如何模拟远程IP地址?


展示如何将上下文分配给测试主题(PageModel) - Nkosi
1
你在哪里得到异常? - Nkosi
在控制器内模拟HttpContext并不是一件简单的事情,因为它是只读属性。您可以在这里了解原因。最好的方法可能是将上下文属性从控制器中抽象出来。 - szachmat
我正在使用 Razor Pages,而不是控制器。这会让它更容易吗? - PixelPaul
实际上是有的!请查看微软文档(https://learn.microsoft.com/en-us/aspnet/core/test/razor-pages-tests?view=aspnetcore-3.1)和示例(https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/test/razor-pages-tests/samples/3.x/tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs)。抱歉,我没有注意到它是RazorPages应用程序。 - szachmat
3个回答

6

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

1
这是我的解决方案。
public static void MockIpAddress(this ControllerBase controller) 
    {
        controller.ControllerContext = new ControllerContext()
        {
            HttpContext = new DefaultHttpContext()
            {
                Connection =
                {
                    RemoteIpAddress = new System.Net.IPAddress(16885952)
                }
            }
        };
    }

0

你的设置与任何调用都不匹配。你还需要模拟连接:

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,以便在调用未正确设置的属性或方法时抛出异常。

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