简体   繁体   English

使用 NUnit 和 NSubstitute 进行单元测试中间件

[英]Unit testing middleware with NUnit and NSubstitute

I've written a bit of middleware in an ASP.NET Core site and I'm trying to unit test it, mainly by following this guide that uses Moq .我在 ASP.NET Core 站点中编写了一些中间件,我正在尝试对其进行单元测试,主要是按照使用Moq 的本指南进行操作。

My problem is finding an NUnit/NSubstitute equivalent for new DefaultHttpContext() .我的问题是为new DefaultHttpContext()找到一个 NUnit/NSubstitute 等价物。 Substituting HttpContext will trigger the middleware, but it passes the try .替换 HttpContext 将触发中间件,但它通过了try I presume this is because of the issue quoted below.我认为这是因为下面引用的问题。 Does NUnit have a function to create a real HttpContext, or am I looking at a lot more infrastructure to achieve this? NUnit 是否具有创建真正 HttpContext 的功能,或者我是否正在寻找更多基础设施来实现这一目标?

I am sending an instance of DefaultHttpContext to the Invoke method.我将 DefaultHttpContext 的一个实例发送到 Invoke 方法。 I can't use a mocked HttpContext in this scenario because the first middleware (the lambda function that we passed to the constructor) will need to write to the response.在这种情况下我不能使用模拟的 HttpContext,因为第一个中间件(我们传递给构造函数的 lambda 函数)需要写入响应。 Hence the HttpResponse needs to be a real object not mocked.因此 HttpResponse 需要是一个未被模拟的真实对象。

Here is the code for my Test这是我的测试代码

[TestFixture]
public class ExceptionHelperTests
{
    private IErrorRepository errorRepository;
    private ExceptionHandler handler;

    [SetUp]
    public void Setup()
    {
        errorRepository = Substitute.For<IErrorRepository>();
    }

    [Test]
    public async void Given_AnExceptionHappens_Then_ItShouldBeLogged()
    {
        // Arrange
        const string username = "aUser";
        var user = Substitute.For<ClaimsPrincipal>();
        user.Identity.Name.Returns(username);

        handler = new ExceptionHandler(
            next: async (innerHttpContext) =>
            {
                innerHttpContext.User = user;
            },
            repository: errorRepository);

        // Act
        await handler.Invoke(new DefaultHttpContext());

        // Assert
        errorRepository.Received().LogException(Arg.Any<string>(), Arg.Any<Exception>(), Arg.Is(username));
    }
}

Here is the IErrorRepository这是 IErrorRepository

public interface IErrorRepository
{
    Exception LogException(string message, Exception ex, string userId);
    void LogMessage(string message, string errorDetail, string userId);
}

And here is the middleware (with a simplified HandleException):这是中间件(带有简化的 HandleException):

public sealed class ExceptionHandler
{
    private readonly RequestDelegate _next;
    private readonly IErrorRepository repository;

    public ExceptionHandler(RequestDelegate next, IErrorRepository repository)
    {
        _next = next;
        this.repository = repository;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            HandleException(ex, context.User.Identity.Name);
        }
    }

    public void HandleException(Exception ex, string userId)
    {
        repository.LogException("An unhandled exception has occurred.", ex, userId);
    }
}

DefaultHttpContext is just the default implementation of HttpContext abstract class. DefaultHttpContext只是HttpContext抽象类的默认实现。

You just could do你只能做

var HttpContextSub = Substitute.For<HttpContext>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM